【学习笔记】重链剖分
关于树链剖分
树链剖分,指一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每个点属于且只属于一条链,然后再通过数据结构来维护每一条链。
树链剖分其实分三种:重链剖分,长链剖分,实虚链剖分 ( ( ( L C T \rm LCT LCT ) ) )。
所以,为了使整体显得更有条理(其实是我还没学完 ),我就分开写了。
重链剖分能做什么
1. 求最近公共祖先 L C A \rm LCA LCA
2. 修改节点 x x x 到节点 y y y 的最短路径上的所有节点 / / /边的权值。
3. 修改以节点 x x x 为根的子树中的所有节点 / / /边的权值。
4. 查询节点 x x x 到节点 y y y 的最短路径上的所有节点 / / /边的权值和或极值。
5. 查询以节点 x x x 为根的子树中的所有节点 / / /边的权值和或极值。
重链剖分的一些定义
定义 s i z e [ x ] \rm size[x] size[x] 为以 x x x 为根的子树的节点个数。
那么,对于每一个非叶节点,其所有儿子中 s i z e \rm size size 最大的儿子便为重儿子(如果有两个或以上都最大的话,则是其中随意一个),而 y y y 其它的儿子,都是轻儿子 。
而一个节点连向其重儿子的边被称为重边,树中重边之外的边被称为轻边。多条重边相连为一条重链。
放张图理解一下
图中灰心点为重儿子,白心点为轻儿子。加粗边为重边。
图中重链有 5 5 5 条: 1 — 3 — 7 — 13 — 16 — 18 , 12 — 15 , 10 — 14 , 2 — 5 , 4 — 8 1—3—7—13—16—18,12—15,10—14,2—5,4—8 1—3—7—13—16—18,12—15,10—14,2—5,4—8。
性质
1. 若 x x x 为轻儿子,则有 s i z e [ x ] \rm size[x] size[x] < = <= <= s i z e [ f a t h e r [ x ] ] 2 \rm \dfrac{size[father[x]]}{2} 2size[father[x]]。
2. 从根到某一点的路径上不超过 log 2 n \rm \log_2 n log2n 条重链,不超过 log 2 n \rm \log_2 n log2n 条轻链。(最重要的性质)
重链剖分的实现
预处理
定义 h s o n i \rm hson_i hsoni 表示节点 i i i 的重儿子编号, s i z i \rm siz_i sizi 表示以节点 i i i 为根的子树的节点个数, d e p i \rm dep_i depi 表示节点 i i i 的深度, f a i \rm fa_i fai 表示节点 i i i 的父亲节点, t o p x \rm top_x topx 表示节点 i i i 位于的重链上的顶端节点标号, d f n i \rm dfn_i dfni 表示 d f s \rm dfs dfs 进入节点 i i i 的时间戳, t t r i \rm ttr_i ttri 为 i i i 在 d f s \rm dfs dfs 序里在树上对应的节点编号。
第一个 d f s \rm dfs dfs 先求出每个节点的 f a i \rm fa_i fai 、 h s o n i \rm hson_i hsoni 、 s i z i \rm siz_i sizi 、和 d e p i \rm dep_i depi。
第二个 d f s \rm dfs dfs 以优先走重边为原则,遍历出 d f s \rm dfs dfs 序,同时求出每个节点的 t o p x \rm top_x topx 、 d f n i \rm dfn_i dfni 和 t t r i \rm ttr_i ttri。
比较重要的两点:
1. 优先走重边——每一条重链的新编号是连续的。
2. d f s \rm dfs dfs 序 ——同一棵子树所对应的一定是dfs序中连续的一段。
还是那张图
P S : \rm PS: PS: 红字表示点的 d f s \rm dfs dfs 序。 d f s \rm dfs dfs 序为: 1 , 3 , 7 , 13 , 16 , 18 , 17 , 12 , 15 , 6 , 10 , 14 , 2 , 5 , 4 , 8 , 9 1,3,7,13,16,18,17,12,15,6,10,14,2,5,4,8,9 1,3,7,13,16,18,17,12,15,6,10,14,2,5,4,8,9。
维护以及在线操作
修改操作
1. 对于修改节点 x x x 到节点 y y y 的最短路径上的所有节点 / / /边的权值,可以像求 L C A \rm LCA LCA一样边跳便更改权值。
2. 至于修改以节点 x x x 为根的子树中的所有节点 / / /边的权值,只需要修改 d f n x \rm dfn_x dfnx 到 d f n x + s i z x − 1 \rm dfn_x+siz_x-1 dfnx+sizx−1 。
查询操作
与修改操作类似,只是把修改变为查询。
时间复杂度?
用线段树来维护树链剖分的时间复杂度是 O ( n + q log 2 2 n ) {\rm O}(n +q\log_2^2n)