树分块小结
作者:互联网
树分块
我们发现我们可以对序列分块,值域分块,时间分块,然后分块的本质就是把大小为 \(n\) 的一个集合划分为 \(O(\frac nB)\) 个大小为 \(O(B)\) 的块,所以我们可以对树也进行分块
转序列分块
一个非常 \(naive\) 的想法是我们求出树的 \(dfs\) 序,然后序列分块,这样可以保证:
1.一个点至多属于一个块(这是因为我们只会在这个点第一次出现的地方保存节点上的值)
2.块内的点是联通的,并且可以知道块的根(也就是块内深度最小的点,容易证明一个块只有一个根)
这个 \(naive\) 的想法如果不用树分块而是用 \(LCT\) 和 \(splay\) 维护的话就变成了 \(ETT\),不过分块可以维护一些 \(polylog\) 做不了的东西,比如区间第 \(k\) 大什么的
当然分块的代价就是和 \(LCT\) 的匹配不丝滑,会多一个 \(\log n\),所以我们考虑纯正的 \(\sqrt n\) 做法,于是我们用块链,然后就完了
不过这样只能插入子树,我并不知道这样如何换根
dfs分块
就是王室联邦题解的做法
我们考虑对于以 \(u\) 为根的子树分块,块的大小为 \(O(B)\),我们就暴力的 \(dfs\),如果一颗子树的大小达到了 \(B\),我们就把它分为一块,否则就把子树里的点都提取出来,和之后的合法子树合并。最后,我们把所有剩下的点放到属于根的块内。
题解里有详细的证明,我就不写了
bfs分块
从根开始 \(bfs\),扫到 \(B\) 个点就建一个块,如果子树大小不到 \(B\) 那也单独建一个块,对于剩下的点构成的森林重复上述过程
这样分出来的块是联通的,但是大小没有保证,大小为 \(\Omega(1),O(B)\)
因为块是联通的,所以我们可以只保留块和块之间的边,建出一颗虚树,可以证明,虚树的深度是 \(O(\frac nB)\) 的,但是大小不知道,我们把每个点在从它在虚树上的点到虚树的根的路径上每一个点都存一遍,修改的时候暴力改就好了
三度化
考虑到一个事实,二叉树有着优秀的性质,于是我们考虑插入一些 \(\text{doomy node}\) 来使得任意树都变成二叉树
如果一个点 \(u\) 的儿子数大于 \(2\),那我们直接每次随便取两个儿子 \(s_1,s_2\) 下来,连到一个虚节点 \(v\) 上,再把 \(v\) 连到 \(u\) 上,\(u\) 的儿子就减少了一个。于是不难证明我们得出的新树的大小还是 \(O(n)\)。
这之后树分块都很容易了,不过这样虽然保证了块的大小为 \(\Omega(B)\),但是不能保证在原树上块内是联通的(所以这个东西有什么用)
标签:子树,联通,分块,虚树,大小,小结,我们 来源: https://www.cnblogs.com/efX-bk/p/tree_divide.html