树链剖分

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\)

    1. 求出 \(siz\)\(dep\)\(son\)\(fa\)

    都好求,正常遍历,\(son\) 是所有儿子 \(siz\) 最大的, 打擂台

    1. 优先跑重链, 标记 \(dfn\) ,求出 \(top\)\(seg\)

求法

    1. 如果没有重儿子,直接跳过(叶子)
    1. 遍历子节点
    1. 走的时候,记录一个 \(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}\)
posted @ 2025-12-29 22:11  wmq2012  阅读(10)  评论(1)    收藏  举报