ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

树链剖分模板 洛谷P3384

2019-09-12 19:05:40  阅读:154  来源: 互联网

标签:cnt 洛谷 剖分 int top deep P3384 ans id


#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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有