树链剖分
Definition
- \(siz\) : 代表以 \(i\) 为根的子树大小
- 重儿子:一个 \(u\) 节点里所有儿子之中的 \(siz\) 最大的那一个(多个任意选)
- 轻儿子:除去重儿子之外的子节点
- 重边:连接一个节点和他的重儿子的边
- 重链:把重边连在一起的变成一条路径 (全是由重边组成)
- 轻链:剩下的部分 (是一个一个的点)
重点
- 严格: 搜索时,优先搜索重儿子
- 性质:
- ① 每条重链的 \(dfn\) 连续 (因为优先走重儿子,所以重链上的点,是一路走下来的)
- ② 子树的 \(dfn\) 连续 (因为会从一个子树内的节点都递归完,在回出来)
- 子树 \(u\) 所在的 \(dfn\) 范围 : \([dfn_u,dfn_u+siz_u-1]\)
U587522 树链剖分-基础知识点
给定一棵以 \(1\) 为根的有根树,现在需要你按照重链剖分的基础知识点,给出每个节点的基本信息,具体如下:
- \(siz[i]\) 表示节点 \(i\) 为根的子树的大小。
- \(son[i]\) 表示节点 \(i\) 所在的重儿子,不存在的话表示为 \(0\) 。
- \(dep[i]\) 表示节点 \(i\) 的深度,其中根节点 \(1\) 的深度是 \(1\) 。
- \(fa[i]\) 表示节点 \(i\) 的父亲节点编号。
- \(dfn[i]\) 表示节点 \(i\) 的 \(dfs\) 序编号,我们规定,根节点 \(1\) 的 \(dfn\) 编号是 \(1\) 。其他点依此类推。此处可能会有多个答案,请按照原顺序使用 \(vector\) 的方式存储。
- \(top[i]\) 表示重链剖分后,该点所处链的链头节点。且 \(top\) 实际上是一个轻儿子(重儿子可以在往上找,串重链)
- \(seg[i]\) 表示重链剖分后,\(dfn\) 为 \(i\) 的节点
对应的初始编号。
剖分
步骤 : 两次 \(dfs\)
-
- 求出 \(siz\) , \(dep\) , \(son\),\(fa\)
都好求,正常遍历,\(son\) 是所有儿子 \(siz\) 最大的, 打擂台
-
- 优先跑重链, 标记 \(dfn\) ,求出 \(top\), \(seg\)
求法
-
- 如果没有重儿子,直接跳过(叶子)
-
- 遍历子节点
-
- 走的时候,记录一个 \(t\), 表示当前重链顶端,这样直接得到 \(top\), 然后走重儿子的时候, 递归下去的 \(t\),不变(还是处于同一条重链),
其他子节点的重链顶端, 就是 它自己
P3379 【模板】最近公共祖先(LCA)
倍增法求是让两个节点一起跳2的幂
用树链剖分是一个一个重链的跳
- 当两个点不处于同一个重链时 (\(top_u \ne top_v\))
- 此时让两个节点所处重链顶端深度更小的那个往上跳 (\(dep_{top_ {u}} \ge\))
- 需要跳的节点跳到所处重链的顶端在往上跳一格(\(u = fa_{top_u}\))
此时两个节点已经处于同一个重链上
而深度更小的就是 \(lca\)
时间复杂度 : 每次停跳了说明遇到了新的重链, 子树的大小倍增, 最多 \(log_n\) 次, 常数非常小
P2590 [ZJOI2008] 树的统计
可以用 跳 \(lca\) 的办法, 把两个节点之间的路径转换为一条一条的重链
由于 同一重链\(dfn\)连续, 所以可以按照每一个节点的 \(dfn\) 建立线段树
用线段树维护最大值
跳重链剩下的一小截,单独处理
P3384 【模板】重链剖分 / 树链剖分
路径的处理和上题一样
子树 :
- 因为子树的 \(dfn\) 是连续的,且子树 \(u\) 所在的 \(dfn\) 范围 : \([dfn_u,dfn_u+siz_u-1]\)
- 同样可以使用线段树!
关于具体是如何根据\(top\) 跳转
- 首先可以根据 \(top\) 的异同判断两个节点是否处于同一个重链上
- 然后每一次具体的跳一条重链, 是先让一个节点跳到它所处的重链顶端 \(top_u\) , 在往上跳一格 \(fa_{top_u}\)

浙公网安备 33010602011771号