[图论]树链剖分
\(1.\)树链剖分的目的
将树形结构转化为线性结构,来维护树上的信息
\(2.\)树链剖分的过程

\((1).\)概念引入:
重儿子:所有儿子中子树节点最多的
链头:树上一条链中深度最小的节点
重链:一条只有链头不是重儿子的链
\((2).\)算法原理:
将一颗树分为若干条重链,在链上进行线性操作(如线段树等),一般用于解决树上路径有关问题
\((3).\)前置数组:
\(f[x]:\)节点\(x\)的父节点,\(sz[x]:x\)的子树中节点个数
\(dep[x]:x\)的深度,\(timetag:\)时间戳,\(dfn[x]:x\)的\(dfs\)序
\(top[x]:x\)所在重链的头(深度最小节点)\(son[x]:x\)的重儿子\(id[x]:dfn\)的反序
\((4).dfs1:\) 求解\(f,son,sz\)
$Code$
void dfs1(int x,int ff){
dep[x]=dep[ff]+1;f[x]=ff;sz[x]=1;
for(int y:e[x]){
if(y==ff) continue;
dfs1(y,x);sz[x]+=sz[y];
if(sz[y]>sz[son[x]]) y=son[x];
//选择重儿子
}
}
\((5).dfs2:\)求解\(dfn,top\)
考虑更改\(dfs\)方式\(dfs(x,t);x\)表示节点编号\(t\)表示该链的链头
\(Case1:x\)是重儿子,则\(top[x]=top[f[x]]\)
\(Case2:x\)不是重儿子,则\(x\)是链头\(top[x]=x\)
$Code$
void dfs2(int x,int t){
dfn[x]=++timetag;id[timetag]=x;
top[x]=t;
if(son[x]) dfs2(son[x],t);//Case1
for(int y:e[x]){
if(y==f[x]||y==son[x]) continue;
dfs2(y,y);//Case2
}
}
若\(top[x]=top[y]\Rightarrow x,y\)在同一条重链上,直接操作即可
否则深度大的向上跳 \(x=f[top[x]]\)同时处理这一条链上的信息
注意:当你在使用边权转点权这一技巧时,注意\(LCA\)不可以计算要在跳同一条链时\(dfn[x]\rightarrow dfn[x]+1\)
$Code$
void modify(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
Seg.update(1,dfn[top[x]],dfn[x]);
x=f[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
if(x!=y)Seg.update(1,dfn[x]+1,dfn[y]);
}
一般结合线段树使用
\(3.\)树链剖分解决的问题
posted on 2026-07-08 10:21 Underthetwilight 阅读(3) 评论(0) 收藏 举报
浙公网安备 33010602011771号