underthetwilight

[图论]树链剖分

\(1.\)树链剖分的目的

将树形结构转化为线性结构,来维护树上的信息

\(2.\)树链剖分的过程

shupo
\((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
	}
}
$(6).$求解$x\rightarrow y$的路径问题

\(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]);
}
$(7).$树剖后的操作

一般结合线段树使用

\(3.\)树链剖分解决的问题

posted on 2026-07-08 10:21  Underthetwilight  阅读(3)  评论(0)    收藏  举报

导航