长链剖分
创建时间:2026-06-28
长链剖分的概念
长链剖分(Long-chain Decomposition)也是树链剖分的一种,与重链剖分类似,每个节点有一个长儿子,表示该节点的子树深度最大的子节点。
长链的求法同重链剖分,用两次 dfs 即可。
void dfs1(int u, int f) {
fa[u] = f;
for (int v : e[u]) {
if (v == f) {
continue;
}
dfs1(v, u);
if (len[v] > len[son[u]]) {
son[u] = v;
}
}
len[u] = len[son[u]] + 1;
}
void dfs2(int u, int t) {
top[u] = t;
if (son[u]) {
dfs2(son[u], t);
}
for (int v : e[u]) {
if (v != fa[u] && v != son[u]) {
dfs2(v, v);
}
}
}
长链剖分的性质:
- 从任意一个节点向上走,经过的长链的长度单调递增。
- 从任意一个节点向上,至多经过 \(O(\sqrt n)\) 条不同长链。
长链剖分的常见应用
快速 \(k\) 级祖先
首先预处理倍增数组 \(fa_{i, j}\) 表示 \(i\) 的 \(2^j\) 级祖先。如果 \(i\) 是某条长链的链头,额外计算 \(dn_{i, j}\) 表示从 \(i\) 沿该长链向下走 \(j\) 步到达的节点,\(up_{i, j}\) 表示向上走 \(j\) 步到达的节点,其中 \(0 \le j < len_i\)。
当查询 \(x\) 的 \(k\) 级祖先时,找出 \(k\) 的二进制最高位 \(h\),令 \(u = fa_{x, h}\),\(t = top_u\)。当 \(k - 2^h \le dep_u - dep_t\) 时,从 \(t\) 沿长链向下走若干步到达 \(x\) 的 \(k\) 级祖先,通过 \(dn_t\) 快速查询;当 \(k - 2^h > dep_u - dep_t\) 时,从 \(t\) 向上走若干步到达 \(x\) 的 \(k\) 级祖先,通过 \(up_t\) 快速查询。根据长链剖分的性质 1,使用 \(dn\) 和 \(up\) 数组不会越界。
该做法的时间复杂度为 \(O(n \log n) \sim O(1)\)。
带权长链剖分
不难想到贪心的思路:维护每个子树内所有可能的路径,把 \(u\) 的点权分配给价值最大的路径。答案为前 \(k\) 大的路径的价值和。
注意到,“把 \(u\) 的点权分配给价值最大的路径”本质上是带点权的长链剖分,直接长链剖分并找到所有链头的 \(len\) 就做完了,使用 nth_element 可以做到 \(\Theta(n)\)。
一个常用结论:一棵树能用 \(k\) 条路径覆盖当且仅当其叶子节点数量 \(\le \lceil \frac k 2 \rceil\)。
扩展练习:CF526G Spiders Evil Plan。
长链剖分优化 DP
不难看出 DP 的基本状态:\(f_{u, i}\) 表示以 \(u\) 为根的子树中,到 \(u\) 的距离为 \(i\) 的节点数量。枚举所有 \(i\) 转移,答案可以在转移过程中求出,时间复杂度 \(O(n^2)\)。
一种思路是重链剖分优化,每个节点继承重儿子的状态,再遍历轻儿子子树内所有节点,并计算贡献。这也就是我们常说的 DSU-on-Tree,它的复杂度为 \(O(n \log n)\)。
另外一种思路是线段树合并,同样可以做到 \(O(n \log n)\),但也不够优秀。
考虑长链剖分。每个节点优先继承轻儿子的状态,对于其它的子节点,暴力枚举距离 \(i\),将 \(f_{v, i}\) 合并至 \(f_{u, i+1}\)。注意到 \(i\) 的有效范围是 \([0, len_v)\),且每条长链只会在链头合并一次,所以总复杂度等于所有长链的长度之和,即 \(\Theta(n)\)。此外,长链剖分优化 DP 通常采用指针的写法,空间复杂度也为 \(\Theta(n)\)。
对比三种做法,不难发现:长链剖分擅长优化子树内与深度、距离有关的 DP,一般这种问题用其他做法也能做,但长链剖分可以把时间复杂度、空间复杂度优化到极致。
扩展练习:P5904 [POI 2014] HOT-Hotels 加强版,P4292 [WC2010] 重建计划,HDU-6634 Salty Fish。

浙公网安备 33010602011771号