其他分享
首页 > 其他分享> > Luogu-P3384 【模板】轻重链剖分/树链剖分

Luogu-P3384 【模板】轻重链剖分/树链剖分

作者:互联网

学习

是看这篇博文学习的。
https://www.cnblogs.com/chinhhh/p/7965433.html

代码

#include <bits/stdc++.h>
using namespace std;

#define MAXN 100005

long long Ha;
int n,m,root;
vector<int> graph[MAXN];


int siz[MAXN];
int son[MAXN];
int fa[MAXN];
int dep[MAXN];
int top[MAXN];
int id[MAXN];
int cnt;
int rk[MAXN];

long long w[MAXN];



struct segment_tree {
	#define LX (X<<1)
	#define RX ((X<<1)|1)
	static const int _MAXN=MAXN;
	
	struct node {
		long long s;
		int l,r,len;
		long long tag;
	} a[_MAXN<<3];
	
	void build(int X, int l, int r) {
		a[X].l=l;
		a[X].r=r;
		a[X].len=r-l+1;
		a[X].tag=0;
		if (l==r) {
			a[X].s=w[rk[l]];
			return;
		}
		build(LX, l, (l+r)>>1);
		build(RX, ((l+r)>>1)+1, r);
		a[X].s=a[LX].s+a[RX].s;
		a[X].s%=Ha;
	}
	
	void down_tag(int X) {
		a[LX].s+=a[X].tag*a[LX].len %Ha, a[LX].s%=Ha;
		a[LX].tag+=a[X].tag, a[LX].tag%=Ha;
		a[RX].s+=a[X].tag*a[RX].len %Ha, a[RX].s%=Ha;
		a[RX].tag+=a[X].tag, a[RX].tag%=Ha;
		a[X].tag=0;
	}
	
	void modify_add(int X, int L, int R, int V)	{
		if (a[X].l>=L && a[X].r<=R) {
			a[X].s+=V*a[X].len %Ha; a[X].s%=Ha;
			a[X].tag+=V; a[X].tag%=Ha;
			return;
		}
		down_tag(X);
		if (a[LX].r>=L) modify_add(LX, L, R, V);
		if (a[RX].l<=R) modify_add(RX, L, R, V);
		a[X].s=(a[LX].s+a[RX].s) %Ha;
	}
	
	long long query_sum(int X, int L, int R) {
		if (a[X].l>=L && a[X].r<=R) {
			return a[X].s;
		}
		long long ret=0;
		down_tag(X);
		if (a[LX].r>=L) ret+=query_sum(LX, L, R);
		if (a[RX].l<=R) ret+=query_sum(RX, L, R);
		
		return ret%Ha;
	}
	
} SegT;



//第一次dfs,计算 fa\dep\size\son 
void dfs1(int X, int Fa)	//(当前节点,父节点)
{
	fa[X]=Fa;
	dep[X]=dep[Fa]+1;
	siz[X]=1;
	int mxsiz=0,mxX=0;
	for (int &o: graph[X]) if (o!=Fa) {
		dfs1(o, X);
		siz[X]+=siz[o];
		if (siz[o]>mxsiz) {
			mxsiz=siz[o];
			mxX=o;
		}
	}
	son[X]=mxX;
}


//第二次dfs,计算 top\id\rk 
void dfs2(int X, int Fa, int T)		//(当前节点,父节点,所在链的顶端节点)
{
	top[X]=T;
	id[X]=++cnt;
	rk[cnt]=X;
	
	if (son[X])
		dfs2(son[X], X, T);		//重儿子 
	
	for (int &o: graph[X]) if (o!=Fa && o!=son[X])
		dfs2(o, X, o);		//普通儿子 
}


void init()
{
	dfs1(root,0);
	dfs2(root,0,root);
	SegT.build(1,0,n);
}


int main()
{
	//freopen("P3384_2.in","r",stdin);
	scanf("%d%d%d%lld",&n,&m,&root,&Ha);
	for (int i=1; i<=n; i++) scanf("%lld",&w[i]);
	for (int i=1,uu,vv; i<n; i++) {
		scanf("%d%d",&uu,&vv);
		graph[uu].push_back(vv);
		graph[vv].push_back(uu);
	}
	
	init();
	
	for (int ttt=1,opr,x,y,z; ttt<=m; ttt++) {
		scanf("%d",&opr);
		
		if (opr==1) {	//路径加 
			
			scanf("%d%d%d",&x,&y,&z);
			while (top[x]!=top[y]) {
				if (dep[top[x]]<dep[top[y]]) swap(x,y);	//让 x 为 top 更深的那个点 
				SegT.modify_add(1, id[top[x]], id[x], z);	//跳上去,区间加 
				x=fa[top[x]];
			}
			//此时 xy 处于同一条链上,路径 x->y 为一段连续区间 
			if (dep[x]>dep[y]) swap(x,y);
			SegT.modify_add(1, id[x], id[y], z);
			
		}
		else if (opr==2) {	//路径求和 
			
			scanf("%d%d",&x,&y);
			long long ans=0;
			while (top[x]!=top[y]) {
				if (dep[top[x]]<dep[top[y]]) swap(x,y);
				ans+= SegT.query_sum(1, id[top[x]], id[x]);
				ans%=Ha;
				x=fa[top[x]];
			}
			if (dep[x]>dep[y]) swap(x,y);
			ans+= SegT.query_sum(1, id[x],id[y]);
			ans%=Ha;
			printf("%lld\n",ans);
			
		}
		else if (opr==3) {	//子树加 
			
			scanf("%d%d",&x,&z);
			SegT.modify_add(1, id[x], id[x]+siz[x]-1, z);
			
		}
		else if (opr==4) {	//子树求和 
			
			scanf("%d",&x);
			printf("%lld\n",SegT.query_sum(1, id[x], id[x]+siz[x]-1));
			
		}
	}
	
	return 0;
}

/*

4 6 1 998244353
0 0 2 4 
2 1
3 1
4 1
3 3 1
1 2 3 1
2 1 1
4 1
1 1 4 6
2 3 1



8 10 2 448348
458 718 447 857 633 264 238 944 
1 2
2 3
3 4
6 2
1 5
5 7
8 6
3 7 611
4 6
3 1 267
3 2 111
1 6 3 153
3 7 673
4 8
2 6 1
4 7
3 4 228


8 10 2 448348
0 0 0 0 0 0 0 0
1 2
2 3
3 4
6 2
1 5
5 7
8 6
3 7 1
4 6
3 1 1
3 2 1
1 6 3 1
3 7 1
4 8
2 6 1
4 7
3 4 1


*/

标签:链剖分,剖分,int,Luogu,long,tag,Ha,id,LX
来源: https://blog.csdn.net/jackypigpig/article/details/120283104