dsu on tree 小结
概述
\(\text{dsu on tree}\) 是一种思想,建立在轻重链剖分之上对暴力进行合并的思想。名为树上启发式合并,还是因为它每次保留了重儿子的信息(可以看成是一种合并)。一般用于处理无修改操作的子树问题。
思路
- 轻重链剖分出重儿子。
- 在树上统计答案。(对于当前访问的 \(x\))
- 在 \(x\) 的轻儿子处统计答案,不保留其贡献。
- 在 \(x\) 的重儿子处统计答案,并保留其贡献。
- 将 \(x\) 的轻儿子的贡献加入。
- 若 \(x\) 是某个点的重儿子,保留其贡献,否则不保留其贡献。
时间复杂度及其证明
时间复杂度是 \(O(n \log n(f(n)+g(n)))\) 的,其中 \(f(n)\) 与 \(g(n)\) 分别为统计答案的时间复杂度与加入贡献的时间复杂度。
证明也很简单。我们知道一个点到根的路径上只会有 \(\log n\) 条轻边。那么,一个点在加入贡献时被它的祖先所访问只会有两种情况:通过一条轻边或通过一条重边。通过轻边会访问 \(\log n\) 次,而通过重边只会访问 \(1\) 次。故时间复杂度就是 \(\log\) 的。
trick
一般可以在树上使用桶来存储一些子树相关的信息,或者使用其他数据结构。思路大体还是和暴力一样,只不过我们希望这个暴力跑的尽可能快(逃
子树相关的信息可以直接维护,但路径相关的信息因为一定在 \(\text{lca}\) 处统计所以我们需要一些技巧。考虑一种处理方式:通过 \(\text{dfs}\) 序保证不会出现在非 \(\text{lca}\) 处统计答案。
例题
CF600E,CF570D,CF208E:使用 \(\text{dsu on tree}\),开桶统计信息即可。
CF375D:开 \(\text{BIT}\) 维护信息或直接维护信息,前者 \(O(n \log^2 n)\) 后者 \(O(n \log n)\)。
[IOI2011]Race:开 \(\text{map}\) 维护从 \(1\to x\) 的路径长度对应的最小深度,统计答案即可。
gym102832F:套路的,处理这种 \(i\oplus j\) 的信息,拆位开桶维护即可。
模板代码
inline void dfs1(int x,int fa) {
sz[x]=1; son[x]=0;
for(register int i=h[x];i;i=ver[i]) {
int y=to[i]; if(y==fa) continue;
dfs1(y,x); sz[x]+=sz[y];
if(sz[son[x]]<sz[y]) son[x]=y;
}
}
inline void upd(int x,int fa,int d) {
...
for(register int i=h[x];i;i=ver[i]) {
int y=to[i]; if(y==fa||y==Son) continue;
upd(y,x,d);
}
...
}
inline void dfs2(int x,int fa,int opt) {
for(register int i=h[x];i;i=ver[i]) {
int y=to[i]; if(y==fa||y==son[x]) continue;
dfs2(y,x,0);
}
if(son[x]) dfs2(son[x],x,1),Son=son[x];
upd(x,fa,1); Son=0;
...
if(opt==0) upd(x,fa,-1);
}

浙公网安备 33010602011771号