DSU on tree 学习笔记
简述
DSU on tree,其实和 dsu(并查集) 没有太大关系,唯一的一点关系可能就是“启发性合并”的思想吧,所以它的中文名称为“树上启发性合并”。
这是一个看起来很暴力的算法。我们拿到一道树上针对顶点询问且无修改的问题,首先考虑如何暴力求解——我们用全局变量存储需要维护的信息,整体 $dfs$ 一遍枚举顶点,对每个枚举的顶点 $dfs$ 它的子树计算这个顶点的答案,当然计算答案前先把全局变量里之前的答案清空。暴力求解的时间复杂度显然是 $O(n^2)$,但我们只需要调节 $dfs$ 的顺序和适时保留全局变量里存的答案就能降低复杂度。
每枚举到一个顶点时,先去计算它的轻儿子的答案,最后再计算重儿子的答案。但是重儿子的答案计算完毕后不再清空全局变量,而是沿用里面的信息去更新它的父节点的答案——这个时候只需要暴力统计轻儿子们对父节点的贡献。
由于从根开始的任意路径上轻边数量不超过 $\lg n$,所以每个点因暴力统计被访问的次数是 $O(\lg n)$ 的,所以 DSU on tree 的复杂度是 $O(n\lg n)$.
模板
流程:
- 预处理重儿子
- $dfs$ 遍历节点
- 先遍历轻儿子,求出答案后消除影响
- 最后遍历重儿子,求出答案后不消除影响
- 回溯后,暴力统计轻儿子对当前节点的答案
- 如果当前节点对于其父节点是轻儿子,则消除影响
代码模板以 Codeforces 600E. Lomsat gelral 为例。
1 | int fa[N], sz[N], son[N]; |
练习
Codeforces 600E. Lomsat gelral
开全局变量 mx,sum,cnt[]
来记录最大数量、最大数量的颜色和、每种颜色的数量。
1 |
|
Codeforces 570D. Tree Requests
开全局变量——bitset<26>b[]
记录每一层的字母数量奇偶性。
1 |
|
SGU 507. Treediff
开全局变量——set<int> s
来记录现在有哪些叶节点的值,更新差值最小值时在 set
里面查前驱后继即可。
1 |
|
Codeforces 208E. Blood Cousins
开全局变量——cnt[]
来记录每个深度的节点数。
另外,这道题还有一种巧妙并且更好写的做法:记录下每个深度有哪些 $dfs$ 序,然后询问就是在某个深度的 $dfs$ 序列中二分找某个区间内的数的数量。
1 |
|
Codeforces 246E. Blood Cousins Return
开全局变量——map<string, int>cnt[N]
来记录每一层某个名字的出现次数。
map<>
的使用:map<string, int>
减到 $0$ 之后 erase()
掉相应的字符串,然后用 size()
就能统计 map
里面有多少键值,即不同的字符串数。
1 |
|
Codeforces 1009F. Dominant Indices
开全局变量——cnt[],mx,mxDep
分别记录每个深度的节点数、最大节点数和最大节点数所在层数。
1 |
|
Codeforces 375D. Tree and Queries
开全局变量——num[],d[]
.
num[c]
记录颜色 $c$ 的数量,d[k]
表示数量大于等于 $k$ 的颜色种数。
1 |
|
DSU on tree 学习笔记
http://xyfjason.github.io/blog-xcpc/2020/04/18/DSU-on-tree-学习笔记/