給你一棵大小為$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個分成一塊。讀取所有的查詢按左端點所在塊排序。重點在於怎麼進行區間轉移...