定义
(不严谨的口胡)
树链剖分将一棵树按照某种方式划分成多条链,再利用一系列数据结构对链上信息进行维护。
有什么用?
支持:
- 求\(LCA\)
- 路径信息维护
- 子树信息维护
- ...
算法实现
怎么实现呢?
因为博主太菜了不会长链剖分,所以这里讲轻重链剖分
据说也有名字叫启发式剖分?
在轻重链剖分中,每个非叶节点有且仅有一个重儿子,其余子节点为轻儿子。轻重儿子划分的依据是子树大小,对于每个非叶节点, 其子树\(size\)最大的子节点成为它的重儿子,其余节点成为它的轻儿子。
放一张图
红色边为重边,黑色边为轻边
性质:
- 每个点在且仅在一条重链上
证明显然 - 对于任意轻边\(edge(u \rightarrow v)\),有\(size[v] < \frac{size[u]}{2}\)
证明显然 - 一棵树从叶节点走到根节点,经过的重链至多有\(log_2(n)\)条
证明:可以发现,经过树剖之后的图是由一些重链和连接它们的轻边组成的(必要时,一条重链也可退化成边甚至是点)。从底向上,当我们每一次走过一条重链并跨过轻边到另一条重链时,所在节点的子树大小都至少翻了一倍(见性质\((2)\)),那么最多翻\(log_2(n)\)次子树大小就会变成\(n\),即跳到根节点。
这条性质的证明实际也是树剖复杂度为严格\(O(nlog_2(n))\)的证明
实现:
两遍dfs
第一遍\(dfs\)维护每个节点的深度\(dep\)、父亲\(fa\)、子树大小\(size\)(回溯时更新),重儿子\(son\)(在更新\(siz\)时顺便维护)
void dfs1(int cur, int fa) {
father[cur] = fa, size[cur] = 1, dep[cur] = dep[fa] + 1;
for (register int i = first[cur]; i; i =