Luogu P3384(树链剖分,裸题)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kidsummer/article/details/82144263

一个代码模板。

#include <bits/stdc++.h>
#define mem(x,v) memset(x,v,sizeof(x)) 
#define go(i,a,b)  for (int i = a; i <= b; i++)
#define og(i,a,b)  for (int i = a; i >= b; i--)
#define MID(a,b) (a + b) / 2
#define lson now << 1
#define rson now << 1 | 1
using namespace std;
typedef long long LL;
const double EPS = 1e-10;
const int INF = 0x3f3f3f3f;
const int N = 1e5+100;
int n,m,r,p; //正常读入。
int lazy[N<<2],val[N<<2]; //线段树。
int cnt = -1,Head[N],Next[N],to[N],w[N],wt[N];//权值,邻接表
int son[N],id[N],f[N],tim,size[N],top[N],dep[N];//树剖数组。
// 重儿子,入队时间戳,父亲节点,子树大小,链的顶端,深度。
int res = 0;
void Add_edge(int u, int v){
	to[++cnt] = v;
	Next[cnt] = Head[u];
	Head[u] = cnt;
}
void dfs1(int u, int fa, int deep){ //解决深度,子树大小,重儿子,父亲节点。
	dep[u] = deep; //deep
	f[u] = fa; size[u] = 1;// father , size of node.
	for (int i = Head[u]; i != -1; i = Next[i]){
		int v = to[i];
		if (v == fa) continue;
		dfs1(v,u,deep+1);
		size[u] += size[v]; //size of u.
		if (size[v] > size[son[u]]) son[u] = v; // 找u 的重儿子。
	}
}
void dfs2(int u, int topf){
	id[u] = ++tim; //时间戳
	wt[tim] = w[u];//权值
	top[u] = topf; //找链顶。
	if (!son[u]) return;
	dfs2(son[u],topf); //先找重儿子,
	for (int i = Head[u]; i != -1; i = Next[i]){ //再找轻儿子。
		int v = to[i];
		if (v == f[u] || v == son[u]) continue; //如果是重儿子,或者父亲节点,跳过。
		dfs2(v,v);
	}
}
// 线段树操作
void push_down(int now, int len){
	if (lazy[now] == 0) return;
	val[lson] = (val[lson] + lazy[now]*(len >> 1)) % p;
	val[rson] = (val[rson] + lazy[now]*(len - (len >> 1))) % p;
	lazy[lson] = (lazy[lson] + lazy[now]);
	lazy[rson] = (lazy[rson] + lazy[now]);
	lazy[now] = 0;
}
void Build(int now, int a, int b){
	if (a + 1 == b){
		val[now] = wt[a] % p;
		return;
	}
	int m = MID(a,b);
	Build(lson,a,m); Build(rson,m,b);
	val[now] = (val[lson] + val[rson]) % p;
}
int Query(int now, int a, int b, int l, int r){
	int ans = 0;
	if (l <= a && r >= b - 1){
		return (val[now] % p);
	}
	int m = MID(a,b);
	push_down(now,b-a);
	if (l < m) ans = (ans + Query(lson,a,m,l,r))%p;
	if (r >= m) ans = (ans + Query(rson,m,b,l,r)) % p;
	return ans;
}

void Insert(int now, int a, int b, int l, int r, int k){
	if (l <= a && r >= b - 1){
		val[now] = (val[now] + k * (b - a))%p;
		lazy[now] += k;
		return;
	}
	push_down(now,b-a);
	int m = MID(a,b);
	if (l < m) Insert(lson,a,m,l,r,k);
	if (r >= m) Insert(rson,m,b,l,r,k);
	val[now] = (val[lson] + val[rson]) % p;
}
// 线段树操作
int qRange(int x, int y){
	int ans = 0;
	while(top[x] != top[y]){
		if (dep[top[x]] < dep[top[y]]) swap(x,y); // 把 x 变成深度大的点。
		ans = (ans + Query(1,1,n+1,id[top[x]],id[x])) % p;//处理路径上的和。
		x = f[top[x]]; //到达链顶的父亲节点,就是换一条链。
	}
	if (dep[x] < dep[y]) swap(x,y);//在同一条链,不一定相等。把x 变成深度大的点。
	ans = (ans + Query(1,1,1+n,id[y],id[x])) % p; //处理路径上的和。
	return ans;
}
void upRange(int x, int y, int k){ //同上。
	k %= p;
	while(top[x] != top[y]){
		if (dep[top[x]] < dep[top[y]]) swap(x,y);
		Insert(1,1,n+1,id[top[x]],id[x],k);
		x = f[top[x]];
	}
	if (dep[x] < dep[y]) swap(x,y);
	Insert(1,1,n+1,id[y],id[x],k);
}
int qSon(int x){
	return Query(1,1,n+1,id[x],id[x]+size[x]-1);
}

void upSon(int x, int k){
	Insert(1,1,n+1,id[x],id[x] + size[x] -1,k);
}

int main(){
	scanf("%d%d%d%d",&n,&m,&r,&p);
	go(i,1,n) scanf("%d",&w[i]);
	mem(Head,-1);
	go(i,2,n){
		int x,y;
		scanf("%d%d",&x,&y);
		Add_edge(x,y); Add_edge(y,x);
	}
	dfs1(r,0,1);
	dfs2(r,r);
	Build(1,1,n+1);
	while(m--){
		int op,x,y,z;
		scanf("%d",&op);
		if (op == 1){
			scanf("%d%d%d",&x,&y,&z);
			upRange(x,y,z);
		} else 
		if (op == 2) {
			scanf("%d%d",&x,&y);
			printf("%d\n",qRange(x,y));
		} else
		if (op == 3){
			scanf("%d%d",&x,&y);
			upSon(x,y);
		} else {
			scanf("%d",&x);
			printf("%d\n",qSon(x));
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/kidsummer/article/details/82144263
今日推荐