浅谈树链剖分

学树链剖分快半年了现在才写总结。

树链剖分

思想

因为一棵树上两个节点之间的信息很不好维护,所以考虑把这棵树分成很多条链维护。
注意到一棵树上子树的 \(DFS\) 序是连续的,但是一条路径不一定连续,所以考虑用一种神秘的方式改变树的 \(DFS\) 序来让这些信息方便维护。
这个方法就是树链剖分。

定义

树链剖分有如下定义:

  1. 重儿子:一个节点的所有子节点中子树最大(重链剖分)/最深(长链剖分)的节点称为该节点的重儿子。
  2. 轻儿子:不是重儿子的点就是轻儿子。
  3. 重边:一个节点到其重儿子的边被称为重边。
  4. 轻边:其它边都是轻边。
  5. 重链:一条极长的重边组成的边就是重链

求解方法

两遍 \(BFS\),第一遍求出每个节点的深度、子树大小、重儿子。第二遍求出 \(DFS\) 序和每条重链。
注意为了方便一般我们都只维护每个节点所在重链的最顶端。

代码
void dfs1(int u, int f) {
    dep[u] = dep[f] + 1, fa[u] = f, siz[u] = 1;
    for (int v : vec[u]) {
        if (v == f) continue;
        dfs1(v, u);
        siz[u] += siz[v];
        if (siz[v] > siz[son[u]]) son[u] = v;
    }
}
void dfs2(int u, int ftop) {
    top[u] = ftop, dfn[u] = ++dfncnt;
    a[dfn[u]] = input[u];
    if (son[u]) dfs2(son[u], ftop);
    for (int v : vec[u]) {
        if (v != son[u] && v != fa[u]) dfs2(v, v);
    }
}

性质

树链剖分后有如下性质:

  1. 每个节点只属于一条重链
  2. 每条重链在 \(DFS\) 序上连续
  3. 重链有 \(O(logn)\) 条。

重链剖分

\(lca\)

两个节点分别往上跳重链,一次跳一条,有限链的顶端更深的。一直跳到两个节点在同一条重链上,此时深度小的节点就是
\(lca\)

路径维护

和求最近公共祖先差不多,每次维护一整条链,最后维护链上的一个区间即可。
如果是维护边的话,就把边权转移到子节点,维护时记得删除 \(lca\) 的贡献。
神秘小优化:对于每一条链单独开一棵线段树,这样修改次数能减小很多。

长链剖分

\(k\) 级祖先

如果 \(k\) 级祖先不在这条长链上就跳链,否则运用倍增的思想求出 \(k\) 级祖先。

posted @ 2026-06-05 14:48  虚空远行者  阅读(2)  评论(0)    收藏  举报