树剖笔记

指路模板

之前学的,但是这种恶心的东西一天不打就忘了。

为了之后会议方便,就简单的写一下吧。

树剖,顾名思义就是把树剖成一条一条的链。但是要按照儿子个数分成轻链重链

每一条链上节点的序号是连续的。可以用线段树或其他数据结构维护信息。

dfs

可以用两个 dfs 解决。

dfs1干的事:

  • 标记每个点的深度 \(dep\)
  • 标记每个点的父亲 \(fa\)
  • 标记每个非叶子节点的子树大小(含它自己)
  • 标记每个非叶子节点的重儿子编号 \(son\)

dfs2干的事:

  • 标记每个点的新编号
  • 赋值每个点的初始值到新编号上
  • 处理每个点所在链的顶端
  • 处理每条链
void dfs1(int x,int f,int deep){
	dep[x]=deep,fa[x]=f,siz[x]=1;
	int maxson=-1;//重儿子的子树大小 
	for(int i=h[x];i;i=nxt[i]){
		int y=ver[i];
		if(y==f)	continue;
		dfs1(y,x,deep+1);
		siz[x]+=siz[y];
		if(siz[y]>maxson)	son[x]=y,maxson=siz[y];
	}
}
void dfs2(int x,int topf){
	id[x]=++cnt,wt[cnt]=w[x],top[x]=topf;
	if(!son[x])	return;
	dfs2(son[x],topf);
	for(int i=h[x];i;i=nxt[i]){
		int y=ver[i];
		if(y==son[x] || y==fa[x])	continue;
		dfs2(y,y);
	}
} 

树上操作:

//以下树上操作 
int ask_range(int l,int r){
	int ans=0;
	while(top[l]!=top[r]){//两点不在同一条链上 
		if(dep[top[l]]<dep[top[r]])	swap(l,r);
		ans=(ans+ask(1,1,n,id[top[l]],id[l]))%p;
		l=fa[top[l]];
	}
	if(dep[l]>dep[r])	swap(l,r);
	ans=(ans+ask(1,1,n,id[l],id[r]))%p;
	return ans;
} 
void change_range(int l,int r,int x){
	x%=p;
	while(top[l]!=top[r]){//基本同上 
		if(dep[top[l]]<dep[top[r]])	swap(l,r);
		change(1,1,n,id[top[l]],id[l],x);
		l=fa[top[l]];
	}
	if(dep[l]>dep[r])	swap(l,r);
	change(1,1,n,id[l],id[r],x);
}

完整代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;

const int N=2e5+5;
int n,m,p;
int e,h[N],nxt[N],ver[N],w[N],wt[N];
//            输入的节点值 ↑  ↑新编号后节点值 
int tr[N<<2],lz[N<<2],len[N<<2];
int son[N],id[N],fa[N],cnt,dep[N],siz[N],top[N];
// 	↑重儿子编号 

void add(int a,int b){
	ver[++e]=b,nxt[e]=h[a],h[a]=e;
}

//线段树
void push(int node,int x){
	tr[node]+=x*len[node]%p;tr[node]%=p;
	lz[node]+=x;lz[node]%=p;
}
void pushup(int node){
	tr[node]=(tr[node<<1]+tr[node<<1|1])%p;
}
void pushdown(int node){
	if(lz[node]){
		push(node<<1,lz[node]);
		push(node<<1|1,lz[node]);
		lz[node]=0;
	}
}
void build(int node,int l,int r){
	len[node]=r-l+1;
	if(l==r){
		tr[node]=wt[l]%p;
		return;
	}
	int mid=(l+r)>>1;
	build(node<<1,l,mid);
	build(node<<1|1,mid+1,r);
	pushup(node);
}

int ask(int node,int l,int r,int be,int en){
	if(r<be || l>en)	return 0;
	if(l>=be && r<=en)	return tr[node];
	pushdown(node);
	int mid=(l+r)>>1;
	return (ask(node<<1,l,mid,be,en)+ask(node<<1|1,mid+1,r,be,en))%p;
}
void change(int node,int l,int r,int be,int en,int x){
	if(r<be || l>en)	return;
	if(l>=be && r<=en){
		push(node,x);
		return;
	}
	pushdown(node);
	int mid=(l+r)>>1;
	change(node<<1,l,mid,be,en,x);
	change(node<<1|1,mid+1,r,be,en,x);
	pushup(node);
}

//---------分割线----------
//以下树上操作 
int ask_range(int l,int r){
	int ans=0;
	while(top[l]!=top[r]){//两点不在同一条链上 
		if(dep[top[l]]<dep[top[r]])	swap(l,r);
		ans=(ans+ask(1,1,n,id[top[l]],id[l]))%p;
		l=fa[top[l]];
	}
	if(dep[l]>dep[r])	swap(l,r);
	ans=(ans+ask(1,1,n,id[l],id[r]))%p;
	return ans;
} 
void change_range(int l,int r,int x){
	x%=p;
	while(top[l]!=top[r]){//基本同上 
		if(dep[top[l]]<dep[top[r]])	swap(l,r);
		change(1,1,n,id[top[l]],id[l],x);
		l=fa[top[l]];
	}
	if(dep[l]>dep[r])	swap(l,r);
	change(1,1,n,id[l],id[r],x);
}
//----------分割线-----------
/*
dfs1干的事: 
- 标记每个点的深度dep[]
- 标记每个点的父亲fa[]
- 标记每个非叶子节点的子树大小(含它自己)
- 标记每个非叶子节点的重儿子编号son[]
*/
void dfs1(int x,int f,int deep){
	dep[x]=deep,fa[x]=f,siz[x]=1;
	int maxson=-1;//重儿子的子树大小 
	for(int i=h[x];i;i=nxt[i]){
		int y=ver[i];
		if(y==f)	continue;
		dfs1(y,x,deep+1);
		siz[x]+=siz[y];
		if(siz[y]>maxson)	son[x]=y,maxson=siz[y];
	}
}
/*
dfs2干的事:
- 标记每个点的新编号
- 赋值每个点的初始值到新编号上
- 处理每个点所在链的顶端
- 处理每条链 
*/
void dfs2(int x,int topf){
	id[x]=++cnt,wt[cnt]=w[x],top[x]=topf;
	if(!son[x])	return;
	dfs2(son[x],topf);
	for(int i=h[x];i;i=nxt[i]){
		int y=ver[i];
		if(y==son[x] || y==fa[x])	continue;
		dfs2(y,y);
	}
} 

int main(){
	
	int root;
	cin>>n>>m>>root>>p;
	for(int i=1;i<=n;++i)	cin>>w[i];
	for(int i=1;i<n;++i){
		int x,y;
		cin>>x>>y;
		add(x,y),add(y,x);
	}
	dfs1(root,0,1);
	dfs2(root,root);
	build(1,1,n);
	
	while(m--){
		int opt,x,y,z;
		cin>>opt;
		if(opt==1){
			cin>>x>>y>>z;
			change_range(x,y,z);
		}
		if(opt==2){
			cin>>x>>y;
			cout<<ask_range(x,y)<<endl;
		}
		if(opt==3){
			cin>>x>>z;
			change(1,1,n,id[x],id[x]+siz[x]-1,z);
		}
		if(opt==4){
			cin>>x;
			cout<<ask(1,1,n,id[x],id[x]+siz[x]-1)<<endl;
		}
	}
	
	return 0;
} 
posted @ 2022-08-03 21:08  _yolanda  阅读(31)  评论(0编辑  收藏  举报