其他分享
首页 > 其他分享> > 树链剖分模板 洛谷P3384

树链剖分模板 洛谷P3384

作者:互联网

#include <cstdio>
const int maxn = 200000+10;

struct edge{
	int v, next;
}e[maxn << 1];


//N、M、R、mod,分别表示树的结点个数、操作个数、根节点序号和取模数
int n, m ,r , mod, len = 1, cnt, code , x, y, z; //cnt是重新进行编号的 编号 
int w[maxn]; //保存每个节点的权值
int wt[maxn]; //保存新编号后节点的权值
int top[maxn];//保存每条重链的开头节点
int id[maxn]; //节点重新编号后的编号
int fa[maxn]; //保存每个节点的父亲节点
int deep[maxn]; //保存每个节点的深度 根节点的深度是1,依次往下递增
int son[maxn]; //记录每个节点的重儿砸是谁
int sz[maxn]; //记录每个节点及其子树的节点数量
int sum[maxn << 2], lazy[maxn << 2]; //线段树维护的区间和   lazy标记
  
int head[maxn]; //默认都是0  表示没有边  
//添加双向边 
void add(int u, int v) {
	e[len].v = v;
	e[len].next = head[u];
	head[u] = len++;  
}
//u是当前节点  fa是父节点(根节点的父节点是0) deep代表节点深度 
void dfs1(int u, int f, int dep) {
	deep[u] = dep; // 记录每个节点深度 
	fa[u] = f;
	sz[u] = 1;
	int sonW = -1; //来记录重儿子的重量
	for (int j = head[u]; j; j = e[j].next) {
		int v = e[j].v;
		if (v == f) continue; 
		dfs1(v, u, dep + 1); 
		sz[u] += sz[v]; //加上儿子的重量 
		//更新重儿子
		if (sonW < sz[v]) {
			son[u] = v;
			sonW = sz[v];
		} 
	} 
}
//给节点重新编号 并计算出重链的top 
void dfs2(int u, int tp) {
	id[u] = ++cnt; //对节点进行重新编号
	wt[cnt] = w[u];//更新新编号后的权值 
	top[u] = tp; //记录当前节点所在重链的top
	if (son[u]) {
		//存在重儿子 先走重儿子这条路
		dfs2(son[u], tp);
	} 
	//创造其他重链, 以其它轻儿子为头
	for (int j = head[u]; j; j = e[j].next) {
		int v = e[j].v;
		if (v == fa[u] || v == son[u]) continue;
		dfs2(v, v); //以自己为top的头 
	} 
}
void puttag(int id, int l, int r, int v) {
	sum[id] += (r - l + 1) * v;
	lazy[id] += v;
} 
void pushdown(int id, int l, int r) {
	if (lazy[id]) {
		int mid = (l + r) >> 1;
		int ll = id << 1;
		int rr = id << 1 | 1;
		lazy[ll] += lazy[id];
		lazy[rr] += lazy[id];
		sum[ll] =  (sum[ll] + (mid - l + 1) * lazy[id]) % mod;
		sum[rr] =  (sum[rr] + (r - mid) * lazy[id]) % mod;
		lazy[id] = 0;
	} 
}

void build(int id, int l, int r) {
	if (l == r) {
		sum[id] = wt[l] % mod	;
		return;
	}
	int mid = (l + r) >> 1;
	build(id << 1, l , mid);
	build(id << 1 | 1, mid + 1, r);
	sum[id] = (sum[id << 1] + sum[id << 1 | 1] )% mod;
} 
void update(int id, int l, int r, int x, int y, int v) {
	if (x <= l && r <= y) {
		puttag(id, l, r, v);
		return;
	}
	//标记下放 
	pushdown(id, l ,r);
	int mid = (l + r) >> 1;
	if (x <= mid) {
		update(id << 1, l, mid, x, y, v);
	} 
	if (y > mid) {
		update(id << 1 | 1, mid + 1, r, x, y, v);
	}
	sum[id] = (sum[id << 1] + sum[id << 1 | 1] )% mod;
}
int query(int id, int l, int r, int x, int y) {
	if (x <= l && r <= y) {
		return sum[id];
	}
	int mid = (l + r) >> 1;
	int ans = 0;
	//下方标记 
	pushdown(id, l ,r);
	if (x <= mid) {
		ans = (ans + query(id << 1, l, mid, x, y)) % mod;
	} 
	if (y > mid) {
		ans = (ans + query(id << 1 | 1, mid + 1, r, x, y)) % mod;
	}
	return ans;
}
void swap(int &x, int &y) {
	int tem = x;
	x = y;
	y = tem;
} 

//1 x y z   x节点到y节点的节点都加上z
void xToyAdd(int x, int y, int z) {
	while (top[x] != top[y]) {
		//把x节点变成深度更深的那个点 
		if (deep[top[x]] < deep[top[y]]) swap(x, y);
		//将当前x的重链上的所有节点都加上z
		update(1, 1, cnt, id[top[x]], id[x], z);
		//x变成该重链头的父亲节点 
		x =  fa[top[x]];
	} 
	//x 与 y节点在同一重链上  把x变成深度小的点  
	if (deep[x] > deep[y]) swap(x, y);
	update(1, 1, cnt, id[x], id[y], z); 
} 
// 2 x y 求x节点到y节点的和 
int xToySum(int x, int y) {
	int ans = 0;
	while (top[x] != top[y]) {
		if (deep[top[x]] < deep[top[y]]) swap(x, y);
		ans = (ans + query(1, 1, cnt, id[top[x]], id[x])) % mod;
		x = fa[top[x]]; 
	}
	if (deep[x] > deep[y]) swap(x, y);
	ans = (ans + query(1, 1, cnt, id[x], id[y])) % mod;
	return ans % mod;
} 
//3 x z 表示将以x为根节点的子树内所有节点值都加上z
void xAdd(int x, int z) {
	update(1, 1, cnt, id[x], id[x] + sz[x] - 1, z);
}
// 4 x 表示求以x为根节点的子树内所有节点值之和
int xSum(int x) {
	return query(1, 1, cnt, id[x], id[x] + sz[x] - 1) ;
}
int main () {
	scanf("%d%d%d%d", &n, &m, &r, &mod);
	for (int i = 1; i <= n; i++) {
		scanf("%d", w + i);
	}
	int u, v;
	for (int i = 1; i < n; i++) {
		scanf("%d%d", &u, &v);
		add(u, v);
		add(v, u);
	}
	//2次dfs  
	dfs1(r, 0, 1); 
	dfs2(r, r);
	//进行建树操作
	build(1, 1, cnt);
	//m行操作
	for (int i = 1; i <= m; i++) {
		scanf("%d", &code);
		if (code == 1) {
			scanf("%d%d%d", &x, &y, &z);
			xToyAdd(x, y, z % mod);
		} else if (code == 2) {
			scanf("%d%d", &x, &y);
			printf("%d\n", xToySum(x, y));
		} else if(code == 3) {
			scanf("%d%d", &x, &z);
			xAdd(x, z % mod);
		} else {
			scanf("%d", &x);
			printf("%d\n", xSum(x)); 
		}
	} 
	return 0;
}

标签:cnt,洛谷,剖分,int,top,deep,P3384,ans,id
来源: https://blog.csdn.net/qq_41280600/article/details/100780687