其他分享
首页 > 其他分享> > 树链剖分

树链剖分

作者:互联网

前言

今天szt学长给我们讲了树剖,听完之后脑子着实嗡嗡的,这什么玩意啊
,本来想做题,但还是想先巩固一下吧。于是这篇文章诞生了…

树剖能干什么?

前置知识

1.线段树,不会请退役。

2.dfs序,不会请自行百度,这里给篇文章

前置概念

树链剖分的预处理

这里需要两个dfs

dfs1:

预处理出每个节点的父亲,子树的大小,节点的深度以及重儿子是谁。

代码如下

void dfs1(int u,int Fa) {
	Size[u]=1;fa[u]=Fa;dep[u]=dep[Fa]+1;
	for(int i=head[u]; i; i=e[i].nxt) {
		int v=e[i].v;if(v==Fa) continue;
		dfs1(v,u); Size[u]+=Size[v];
		if(Size[son[u]]<Size[v]) son[u]=v;
		//如果该节点的子树大小比当前记录的重儿子的子树大小大,更新
	}
}

dfs2

void dfs2(int u,int Top) {
	top[u]=Top;dfn[u]=++tot;pre[tot]=u;
	//top记录该链的链顶是哪个节点
	//pre记录dfs序 
	//dfn记录节点在dfs序中的位置 
	if(son[u]) dfs2(son[u],Top);
	//如果有重儿子先遍历重儿子所在的链。
	for(int i=head[u]; i; i=e[i].nxt) {
		int v=e[i].v;
		if(v==fa[u] or son[u]==v) continue;
		dfs2(v,v);
		//非父非重儿子,即为轻链,节点和链顶都是自己。
	}
	return; 
}

有点糊,在经szt的确认下,可以这么理解,只把重链当成一条链,轻链仅仅包含其所在的一条边。

树链剖分的操作

通过刚才的预处理,我们已经把树上节点的新编号变成了连续的。然后我们就可以针对区间(链)把原树变成线段树来进行我们要做的一系列操作。

一份线段树代码(模板题上粘的有取模请自行无视):

namespace segment{
    struct node{
    	int len,lazy,sum;
	}tree[N<<2];
	#define lson pos<<1
	#define rson pos<<1|1
	void build(int pos,int l,int r){
		tree[pos].len=(r-l+1);
		if(l==r){
			tree[pos].sum=a[pre[l]]%p;
			return;
		}
		int mid=l+r>>1;
		build(lson,l,mid);build(rson,mid+1,r);
		tree[pos].sum=(tree[lson].sum+tree[rson].sum)%p;
		return;
	}
	
	void push_down(int pos){
		if(tree[pos].lazy){
			tree[lson].lazy=(tree[pos].lazy+tree[lson].lazy)%p;
			tree[rson].lazy=(tree[pos].lazy+tree[rson].lazy)%p;
			tree[lson].sum=(tree[lson].sum+tree[pos].lazy*tree[lson].len)%p;
			tree[rson].sum=(tree[rson].sum+tree[pos].lazy*tree[rson].len)%p;
			tree[pos].lazy=0;
		}
	    return;
	}
	
	void change(int pos,int l,int r,int L,int R,int k){
		if(l>=L&&r<=R){
			tree[pos].sum=(tree[pos].sum+tree[pos].len*k)%p;
			tree[pos].lazy+=k;
			return;
		} if(l>R or r< L) return;
		push_down(pos);int mid=l+r>>1;
		if(L<=mid) change(lson,l,mid,L,R,k);
		if(R>mid) change(rson,mid+1,r,L,R,k);
		tree[pos].sum=(tree[lson].sum+tree[rson].sum)%p;
	    return;
	}
	
	int query(int pos,int l,int r,int L,int R){
		if(l>=L&&r<=R) return tree[pos].sum%p;
		push_down(pos);int mid=l+r>>1,res=0;
		if(L<=mid) res=(res+query(lson,l,mid,L,R)%p)%p;
		if(R>mid) res=(res+query(rson,mid+1,r,L,R)%p)%p;
		return res%p;
	}
} 

唯一的不同就在 \(build\) 当中的 $ tree[pos].sum=a[pre[l]]$ 了,意思就是pos这个点对应dfs序的点的值。

好像很绕,给个szt的图感受一下?

貌似可以理解。

标签:lazy,剖分,int,sum,tree,pos,树链,节点
来源: https://www.cnblogs.com/BlackDan/p/15707452.html