树链剖分

写在前面

某位菜鸡花了半天的时间打完了树剖,又用了半天的时间di了无数个bug后,终于获得了MLE,RE并存的喜人成绩,最终在 \(ljc\) 大佬的指点下才发现原来是函数void写成int了并且没写返回值 T_T

正题

懒得自己写一篇博客了,就把我当时学习树剖时的一篇写的非常好的博客拿出来欣赏吧
博客原文

几条重要的规则

  • 一个字树内的dfs序连续,线段树维护3,4操作
  • 节点数多的叫做重儿子,重儿子到父亲节点的边叫做重边
  • 每一条链dfs序连续(先遍历重儿子)
  • 跳到同一条重链上深度小的即为最近公共祖先
dfs1():

统计每个节点的深度 \(deep[ ]\) ,每个节点父亲 \(fa[ ]\) ,每个节点子树大小 \(size[ ]\) ,重儿子编号 \(mson[]\)

dfs2():

每个旧节点新编号 \(id[]\) ,每个新节点的旧编号 \(di[]\) ,新编号的值 \(newa[]\) ,每个节点所在重链顶端 \(top[]\)

警示后人

一定要处理好新编号和旧编号的关系,最好采用( \(id\)\(di\) )双重索引,注意你的变量是哪一次dfs()维护的,注意新旧节点的转换

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
vector<int>b[N];
int deep[N],size[N],fa[N],mson[N],a[N],newa[N],top[N],id[N],t[4*N],add[4*N],di[N];
int cnt,n,m,st,v,p,u,op,z;
void dfs1(int x,int y){//节点是x,父亲是y 
	deep[x]=deep[y]+1;
	fa[x]=y;
	int maxson=0;
	for(auto i:b[x]){
		if(i==y)  continue;//双向建边的危害
		dfs1(i,x);
		if(size[i]>maxson){
			mson[x]=i;
			maxson=size[i];
		}
	} 
	size[y]+=size[x];
}
void dfs2(int x,int topy){
	if(x==0)  return;//叶子节点访问记得特判
	id[x]=++cnt;
	di[cnt]=x;
	newa[cnt]=a[x];
	top[cnt]=id[topy];
	dfs2(mson[x],topy);
	for(int i:b[x]){
		if(i==fa[x])  continue;
		if(i==mson[x]) continue;
		dfs2(i,i);
	}
}
void build(int k,int l,int r){
	if(l==r){
		t[k]=newa[l];
		return;
	}
	int mid=(l+r)>>1;
	build(2*k,l,mid);
	build(2*k+1,mid+1,r);
	t[k]=t[2*k]+t[2*k+1];
	t[k]%=p;
}
void Add(int k,int l,int r,int vv){//int 大寄
	t[k]+=vv*(r-l+1);
	t[k]%=p;
	add[k]+=vv;
	add[k]%=p;
}
void pushdown(int k,int l,int r){
	int mid=(l+r)>>1;
	Add(2*k,l,mid,add[k]);
	Add(2*k+1,mid+1,r,add[k]);
	add[k]=0;
}
int longqure(int k,int l,int r,int x,int y){
	if(x<=l&&r<=y){
		return t[k];
	}
	pushdown(k,l,r);
	int mid=(l+r)>>1;
	int res=0;
	if(x<=mid)  res+=longqure(2*k,l,mid,x,y);
	if(mid<y)  res+=longqure(2*k+1,mid+1,r,x,y);
	res%=p;
	return res;
}
void longchange(int k,int l,int r,int x,int y,int vv){
	if(x<=l&&r<=y){
		t[k]+=vv*(r-l+1);
		t[k]%=p;
		add[k]+=vv;
		add[k]%=p;
		return;
	}
	pushdown(k,l,r);
	int mid=(l+r)>>1;
	if(x<=mid)  longchange(2*k,l,mid,x,y,vv);
	if(mid<y)  longchange(2*k+1,mid+1,r,x,y,vv);
	t[k]=t[2*k]+t[2*k+1];
	t[k]%=p;
	return;
}
int lca(int x,int y,int vv,bool cz){
	int res=0;
	while(top[x]!=top[y]){
//		printf("x=%d y=%d\n",x,y);
		if(deep[di[top[x]]]<deep[di[top[y]]])  swap(x,y);
		if(cz)  longchange(1,1,n,top[x],x,vv);
		else  res+=longqure(1,1,n,top[x],x);
//		printf("lca=     %d %d %d %d %d %d\n",di[top[x]],di[x],di[y],top[x],x,y);
		x=id[fa[di[top[x]]]];
//		printf("fa=%d\n",x);
	}
//	printf("x=%d y=%d\n",x,y);
	if(deep[di[x]]>deep[di[y]])  swap(x,y);
	if(cz)  longchange(1,1,n,x,y,vv);
	else  res+=longqure(1,1,n,x,y);
//	printf("lca=     %d %d %d %d\n",di[x],di[y],x,y);
	return res;
}
int main(){
	scanf("%d%d%d%d",&n,&m,&st,&p);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		size[i]=1;
	}
	for(int i=1;i<n;i++){
		scanf("%d%d",&u,&v);
		b[u].push_back(v);//双向建边
		b[v].push_back(u);
	}
	dfs1(st,0);
	dfs2(st,st);
	build(1,1,n);
//	for(int i=1;i<=n;i++){
//		printf("i=%d id=%d a=%d t=%d s=%d\n",i,id[i],newa[i],top[i],id[mson[i]]);
//	}
	for(int i=1;i<=m;i++){
		scanf("%d",&op);
		if(op==1){
			scanf("%d%d%d",&u,&v,&z);
			lca(id[u],id[v],z,1);
		} 
		if(op==2){
			scanf("%d%d",&u,&v);
			printf("%d\n",lca(id[u],id[v],0,0)%p);
		}
		if(op==3){
			scanf("%d%d",&u,&z);
//			printf("u=%d %d\n",id[u],size[u]);
			longchange(1,1,n,id[u],id[u]+size[u]-1,z);
		}
		if(op==4){
			scanf("%d",&u);
			printf("%d\n",longqure(1,1,n,id[u],id[u]+size[u]-1)%p);
		}
//		for(int i=1;i<=n;i++){
//			printf("             %d %d\n",i,longqure(1,1,n,id[i],id[i]));
//		}
	}
}
posted @ 2024-08-04 08:14  daydreamer_zcxnb  阅读(21)  评论(0编辑  收藏  举报