首页 > 其他分享 >树链剖分学习笔记(1)

树链剖分学习笔记(1)

时间:2023-03-21 18:13:49浏览次数:39  
标签:剖分 dep ll 笔记 树链 dfn 节点 son top

两大DFS

树链剖分是一个比较简单易懂的算法,其两个基础操作为两次dfs,第一次dfs求出每个节点的父节点(\(f_{i}\)),深度(\(dep_{i}\)),子树大小(\(size_{i}\)),重儿子(\(son_{i}\))。其中,重儿子是其子节点中字数最大的,所以不难写出第一次dfs的代码:

void dfs1(ll u,ll fa){
	f[u]=fa;
	dep[u]=dep[fa]+1;
	siz[u]=1;
	for(int i=0;i<e[u].size();i++){
		ll v=e[u][i];
		if(v!=fa){
			dfs1(v,u);
			siz[u]+=siz[v];
			if(siz[v] > siz[son[u]] || son[u]==0){
				son[u]==v;
			}
		}
	} 
} 

第二次dfs,就要拆树成链了。以重儿子优先的顺序来dfs遍历一遍树,记下每个点的新编号(\(dfn_{i}\)),新编号所对应的旧编号(\(rk_{i}\)),以及每一条重链的链顶。

void dfs2(ll u,ll t){
	top[u]=t;dfn[u]=++num;rk[num]=u;
	if(son[u]){
		dfs2(son[u],t);
	}
	for(int i=0;i<e[u].size();i++){
		ll v=e[u][i];
		if(v!=son[u] && v!=f[u]){
			dfs2(v,v);
		}
	}
} 

拆树成链之后可以做些什么

显而易见,同一条重链中的节点的\(dfn\)编号是连续的,所以我们就把一棵树拆成了若干连续的区间。不难想到,可以用线段树来维护这些连续的区间。因此,树链剖分最简单的应用:对树上两节点间的最短路径路径上面的节点进行区间修改和查询,没错,这运用了倍增的思想

注意:倍增的时候应跳低的链顶,即深度更大的那个。
void work(ll x,ll y,ll z){
	while(top[x]!=top[y]){
		if(dep[top[x]] < dep[top[y]]){
			swap(x,y);
		}
		//update or query
		x=f[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	//update or query
}

一些注意事项

我们是以\(dfn\)序来建树的,因此,建树过程中的叶子节点应赋予其所对应的原\(dfs\)序的值,即:

if(l==r){
		t[rt]=base[rk[l]];
        return;
	}

使用线段树操作时要把节点转换成\(dfn\)序下标

update(1,1,n,dfn[top[x]],dfn[x],z);

线段树别忘了return,否则会RE

标签:剖分,dep,ll,笔记,树链,dfn,节点,son,top
From: https://www.cnblogs.com/mikufun4405/p/17240892.html

相关文章