SPOJ COT2 (樹上莫隊)

2022-07-13 09:57:07 字數 2018 閱讀 6312

給你一棵大小為$n$的樹,每個點都有點權。現在有$m$個詢問,每個詢問給你乙個兩個數$a,b$,問你從點$a$到點$b$之間的路徑中不同的點權的個數。

萬惡的spoj並沒有寫點權的資料範圍,害我我先re(此題需要離散化點權)

求解帶有詢問的不同數的個數這類題,~~一看就相當莫隊~~ ,但是因為莫隊只能夠在乙個序列上進行操作,因此我們考慮如何讓樹的樹形結構轉化為線性的結構。

我們考慮使用樹的尤拉序。

我們設$st$為第一次遍歷經過的編號,$en$為回溯時經過的編號,則現在有兩種方案:

1. 如果兩個節點$a,b$在同一條鏈上,則我們直接選取尤拉序區間$([st[a],st[b])$

2. 如果兩個結點不在同一條鏈上,則我們直接選取尤拉序區間$(en[a],st[b])$

對於每個區間,我們只需要統計該區間中,數字只出現一次的數字的個數。

與此同時,因為在操作$2$中,兩個節點的最近公共祖先是沒有被貢獻的,因此我們還得加上$lca(a,b)$的貢獻。

之後就是我們普通莫隊的分塊了。

#include

#define maxn 200005

#define k 20

using

namespace std;

struct pq[maxn]

;struct nodeq[maxn<<1]

;int lim,cnt,pos[maxn<<1]

,c[maxn]

;int n,m,loc[maxn<<1]

,dfn,st[maxn]

,en[maxn]

,d[maxn]

,f[maxn][25

];int ans[maxn]

,cnt[maxn]

,sum,head[maxn]

;bool vis[maxn]

;bool

cmp(

const p&a,

const p&b)

void

add_edge

(int from,

int to)

void

dfs(

int x)

loc[en[x]

=++dfn]

=x;}

intlca

(int x,

int y)

return f[x][0

];}void

deal

(int x)

elseif(

!(cnt[c[x]]++

)) sum++

; vis[x]^=

1;}int d[maxn]

;int

main()

sort

(d+1

,d+1

+n);

for(

int i=

1;i<=n;i++

) c[i]

=lower_bound

(d+1

,d+1

+n,c[i]

)-d;

for(

int i=

1;i)dfs

(d[1]=

1),lim=

(int

)sqrt

(n*2

+0.5);

for(

int i=

1;i<=dfn;i++

) pos[i]

=(i-1)

/lim+1;

for(

int i=

1;i<=m;i++

)sort

(q+1

,q+1

+m,cmp)

;int l=

1,r=0;

for(

int i=

1;i<=m;i++

)for

(int i=

1;i<=m;i++

)printf

("%d\n"

,ans[i]);

}

SPOJ COT2 樹上莫隊

以下路徑都不包含 lca lca 考慮當前知道路徑 u v u,v 的資訊,想要知道 t v t,v 的資訊,只需把 u t u,t 上的點狀態取反即可,那麼複雜度是和 u t u,t 的長度相關的。於是我們考慮把樹分塊,每當乙個點的子樹大小 n n就拎出來成為一塊。id i idi 表示i i 號...

SPOJ COT2 (樹上莫隊)

給你一棵大小為n nn的樹,每個點都有點權。現在有m mm個詢問,每個詢問給你乙個兩個數a,b a,ba,b,問你從點a aa到點b bb之間的路徑中不同的點權的個數。萬惡的spoj並沒有寫點權的資料範圍,害我我先re 此題需要離散化點權 求解帶有詢問的不同數的個數這類題,一看就相當莫隊 但是因為莫...

SPOJ COT2 樹上的莫隊演算法,樹上區間查詢

題意 n個節點形成的一棵樹。每個節點有乙個值。m次查詢,求出 u,v 路徑上出現了多少個不同的數。樹上的莫隊演算法,同樣將樹分成siz sqrt n 塊,然後離線操作。先對樹dfs一遍,每當子樹節點個數num siz,就將這num個分成一塊。讀取所有的查詢按左端點所在塊排序。重點在於怎麼進行區間轉移...