树链剖分

树链剖分介绍

树链剖分是什么?主要用来处理什么问题

树链剖分(Heavy-Light Decomposition, HLD)


1. 树链剖分的定义

树链剖分是一种 把树分解成若干条链 的算法技巧。
在一棵树上,我们把每个节点的“重儿子”挑出来(通常是子树规模最大的儿子),把它和父亲连成 重边,其它边就是 轻边

这样整棵树就被拆分成若干条 重链,并且有一个关键性质:

  • 从根到任意一个节点的路径上,**轻边的数量至多是 \(O(\log n)\) **。

因此,任何树上的一条路径,都可以用 若干条( \(\log n\) 级别)连续的链 来表示。


2. 树链剖分的主要用途

它的核心目标是把 树上的路径/子树问题 转化为 线段树/树状数组上的区间问题,从而可以在 \(O(\log^2 n)\) 的时间里解决很多复杂问题。

常见应用有:

  1. 路径查询
    • 比如:查询树上两点路径上的最大值、最小值、和、异或值……
    • 利用树链剖分把路径分解成若干段连续区间,用线段树维护。
  2. 路径修改
    • 比如:把树上某条路径上的点权/边权全部加上一个数。
    • 仍然是拆成若干区间,交给线段树做区间修改。
  3. 子树查询 / 修改
    • 如果我们按照 DFS 序(欧拉序)编号,每个子树对应一个连续区间。
    • 可以直接用线段树维护子树问题。
  4. 更复杂的树上问题
    • 树上差分、树上 DP 的优化。
    • 结合树剖 + 数据结构,可以处理动态树上的很多操作。

树链剖分前置知识是什么

树链剖分(HLD) 之前,最好先掌握一些 图论 + 数据结构的基础知识,这样学习起来会轻松很多。

我给你分几个层次讲:


1. 树的基本知识 🌳

树链剖分是树上的高级技巧,所以树的基础必须牢固。
需要掌握的内容有:

  • DFS遍历
    • 如何用 DFS 遍历树,记录进入时间 / 退出时间(欧拉序)。
  • 子树大小
    • 会写 dfs(u) 统计每个节点子树大小。
  • 父亲与深度
    • 在 DFS 时记录 fa[u](父亲节点)和 dep[u](深度)。

👉 这些是后续判断“重儿子”和路径分解的核心。


2. 最近公共祖先 LCA

为什么要学 LCA?

  • 树链剖分常常需要把路径 \((u,v)\) 拆解成 \((u,LCA(u,v))\)\((v,LCA(u,v))\)
  • 当然,在 HLD 里,LCA 也能用树剖自带的 top[] 数组来求,但原理和 LCA 高度相关。

所以至少要会:

  • 倍增法 LCA (O(log n) 查询)。
  • 或 Tarjan 离线 LCA。

3. 数据结构:线段树 / 树状数组

树链剖分的最终目标是 把树上路径/子树问题转化为区间问题
所以你必须熟悉:

  • 线段树:区间修改、区间查询。
  • 懒标记:比如区间加法、区间乘法、区间取最大值等。

如果连线段树都不会写,HLD 基本没法落地。


4. DFS 序(欧拉序)与子树映射

你需要理解:

  • 在 DFS 时给每个节点一个时间戳 dfn[u]
  • 子树的所有节点编号是 一段连续区间
    • 例如,[dfn[u], dfn[u] + size[u] - 1] 对应 u 的整棵子树。
  • 这是处理 子树查询/修改 的关键。

5. 树剖专属概念

等你有了以上基础,就能自然地理解树剖里的几个数组:

  • son[u]:重儿子(子树最大)
  • top[u]:当前重链的链顶
  • dfn[u]:DFS 序编号
  • rk[pos]:DFS 序对应的节点
  • fa[u], dep[u], siz[u]:父亲、深度、子树大小

这些概念本质上都是 DFS + 子树大小 的应用。


✅ 总结

树链剖分的前置知识:

  1. 树的基本 DFS(父亲、深度、子树大小)
  2. 最近公共祖先(LCA) 的思想
  3. 线段树/树状数组(区间修改 + 区间查询)
  4. DFS 序与子树映射
  5. 熟悉 基本图论建图(邻接表存树,dfs 遍历)
posted @ 2025-09-18 10:15  katago  阅读(19)  评论(0)    收藏  举报