初涉DSU on tree

早先以为莫队是个顶有用的东西,不过好像树上莫队(不带修)被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 }

就这些

题目

猜你喜欢

转载自www.cnblogs.com/antiquality/p/9188930.html