1. 程式人生 > >初涉DSU on tree

初涉DSU on tree

ref 16px one 就是 後來 大量 == 我們 註意

早先以為莫隊是個頂有用的東西,不過好像樹上莫隊(不帶修)被dsu碾壓?

dsu one tree起源

dsu on tree是有人在cf上blog上首發的一種基於輕重鏈剖分的算法,然後好像由因為這個人後來在cf上辦了場比賽出了道dsu on tree的裸題由此出名?

這個是原博客地址:http://codeforces.com/blog/entry/44351

大概思想就是一種樹上啟發式合並,利用輕重鏈剖分把重復計算的答案利用起來,從而把時間復雜度控制在$O(n log n)$(不過不能修改)。

註意dsu的一大特點:一次dsu之後是把整棵樹的答案都處理出來,因此它更適合大量查詢的情況。

下面講一下算法流程:

1.預處理樹的結構,把$fa$,$son$,$tot$處理出來。具體操作參見樹剖。

2.為了統計當前節點答案,先遞歸處理所有輕兒子。然後遞歸回當前節點時,其子樹內除了重兒子都已經處理好答案了。

3.如果有重兒子,那麽遞歸重兒子同時標記重兒子這個節點

4.現在子樹信息都已經處理好了,考慮向上合並信息。我們把子樹內所有點都統計顏色一遍(除了重兒子的家族)

5.現在子樹信息傳遞好了

6.如果這個節點是輕兒子轉移過來的,那麽清除這顆子樹所有信息(包括子樹裏的重兒子)

 1 void color(int x, int c)    //把x的子樹都統計一遍
 2 {
3 cnt[a[x].col] += c; 4 if (c>0&&mx <= cnt[a[x].col]) 5 { 6 if (mx < cnt[a[x].col]) mx = cnt[a[x].col], sum = 0; 7 sum += a[x].col;    //如果現在是 8 } 9 for (int i=0; i<f[x].size(); i++) 10 if (f[x][i]!=a[x].fa&&!vis[f[x][i]])    //處理兒子節點
11 color(f[x][i], c); 12 } 13 void dfs2(int x, bool fl)      //dsu:x表示當前節點  fl表示當前節點是輕兒子還是重兒子 14 { 15 for (int i=0; i<f[x].size(); i++) 16 if (f[x][i]!=a[x].son&&f[x][i]!=a[x].fa) 17 dfs2(f[x][i], 0); 18 if (a[x].son!=-1) dfs2(a[x].son, 1), vis[a[x].son] = 1; 19 color(x, 1);  //c==1是統計答案;c==0是消除答案 20 ans[x] = sum; 21 if (a[x].son!=-1) vis[a[x].son] = 0; 22 if (!fl) color(x, -1), mx = sum = 0; 23 }

就這些

題目

初涉DSU on tree