30分全注释版树剖代码,求查错
查看原帖
30分全注释版树剖代码,求查错
236912
QianShao楼主2020/6/18 16:05

一份30分(1,3,4)的代码,其余点WA(2,5,6,7,8,9,10)掉。 代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;

const int N = 1e5 + 5;
int n, m, r, mod, hd[N], tot, w[N], rank[N], tim;
long long val[N << 2], tag[N << 2];

// n :  节点个数
// m : 操作个数
// r : 根节点序号
// mod : 取模数
// hd[x] : <链式前向星> 节点x最后一条边的编号
// tot : <链式前向星> 记录总边数
// w[x] : 节点x的初始值
// rank[x] : 时间戳为x的节点编号
// tim : 时间标记 

struct Node{ // 节点参数 
	int fa, dep, siz, hson, top, dfn, low; 
	// fa : 父节点编号 
	// dep : 深度
	// siz : 子树大小
	// hson : 重儿子编号
	// top : 所在重链链首
	// dfn : 时间戳
	// low : 子树遍历结束的时间戳 
}node[N];

struct Edge{ // 边参数 
	int ed, nxt;
	// ed : 终点
	// nxt : 上一条边 
}edge[N << 1];

void add(int x, int y) { // 链式前向星存图 
	edge[++tot].ed = y;
	edge[tot].nxt = hd[x];
	hd[x] = tot;
}

// 线段树部分 

void build(int p, int l, int r) { // 建树 
	if (l == r)	return (void) (val[p] = w[rank[l]]); // 叶子结点的权值是第 l (或 r ) 个时间戳对应的节点 
	int mid = l + r >> 1;
	build(p << 1, l, mid); // 左儿子 
	build(p << 1 | 1, mid + 1, r); // 右儿子 
	val[p] = (val[p << 1] + val[p << 1 | 1]) % mod; // pushup
	return;
}

void pushdown(int p, int l, int r) { // 下放lazytag 
	tag[p << 1] = (tag[p] + tag[p << 1]) % mod, tag[p << 1 | 1] = (tag[p] + tag[p << 1 | 1]) % mod; // tag直接传到儿子 
	int mid = l + r >> 1;
	val[p << 1] = (tag[p] * (mid - l + 1) + val[p << 1]) % mod,
	val[p << 1 | 1] = (tag[p] * (r - mid) + val[p << 1 | 1]) % mod; // val值加上区间个数乘tag 
	tag[p] = 0; // 父节点tag清空 
}

void modify(int p, int l, int r, int L, int R, int k) { // 修改时间戳为 L 到 R 区间的值,当前编号p, 区间l到r 
	if (l >= L && r <= R) { // 如果当前区间为待修改区间的子区间 
		tag[p] = (tag[p] + k) % mod; // lazytag 加上 k 
		val[p] = (val[p] + (r - l + 1) * k) % mod; // val 加上区间长度乘k 
		return;
	}
	if (tag[p])	pushdown(p, l, r); // 下放标记 
	int mid = l + r >> 1;
	if (mid >= L)	modify(p << 1, l, mid, L, R, k); // 如果左儿子含有待修改区间,修改左儿子 
	if (mid < R)	modify(p << 1 | 1, mid + 1, r, L, R, k); // 如果右儿子含有待修改区间,修改右儿子 
	val[p] =(val[p << 1] + val[p << 1 | 1]) % mod; // pushup
	return;
}

long long query(int p, int l, int r, int L, int R) { // 询问时间戳为 L 到 R的节点加和, 当前节点编号p, 区间l到r 
	if (l >= L && r <= R)	return val[p]; // 该区间为询问区间的子区间 
	if (tag[p])	pushdown(p, l, r); // 下放标记 
	int mid = l + r >> 1;
	long long ans = 0;
	if (mid >= L)	ans = query(p << 1, l, mid ,L, R); // 如果左儿子含有询问区间,ans加上左儿子的贡献 
	if (mid < R)	ans = (ans + query(p << 1 | 1, mid + 1, r, L, R)) % mod; // 如果右儿子含有询问区间, ans加上右儿子的贡献 
	return ans;
}

// 以下为树剖函数 

void TREE_build(int u, int des) { // 以深度为des的节点u为根节点,建树,统计子树大小并确定重儿子 
	node[u].siz = 1; // 初始大小为1 
	for (int i = hd[u]; i; i = edge[i].nxt) { // 遍历边 
		int v = edge[i].ed; // 取该边终点 
		if (v == node[u].fa)	continue; // 如果扫到父节点,就跳过 
		node[v].fa = u; // 将v的父节点设为u 
		node[v].dep = des + 1; // 子节点深度为父节点深度加1 
		TREE_build(v, des + 1); // 建儿子 
		node[u].siz += node[v].siz; // 父节点大小加上子节点大小 
		if (!node[u].hson || node[v].siz > node[node[u].hson].siz)	node[u].hson = v;
		// 如果父节点还没有重儿子 或 该儿子的子树大小大于原有的重儿子, 更新重儿子 
	}
}

void TREE_decomposition(int u, int top) { // 当前节点为u,所在重链的链顶为top,划分重链并标记时间戳 
	node[u].top = top; // 记录点u链顶为top 
	node[u].dfn = ++tim; // 记录时间戳 
	rank[tim] = u; // 记录时间戳为tim的节点为u 
	if (node[u].hson)	TREE_decomposition(node[u].hson, top); // 如果有重儿子,传递链顶 
	for (int i = hd[u]; i; i = edge[i].nxt) { // 遍历轻儿子 
		int v = edge[i].ed; // 取该边终点 
		if (v == node[u].hson || v == node[u].fa)	continue; // 如果终点为父节点或重儿子,就跳过 
		TREE_decomposition(v, v); // 对轻儿子,以其自身为链顶继续剖分新的重链 
	}
	node[u].low = tim; // 以u为根的子树最后一个节点的时间戳 
}

void TREE_path_modify(int u, int v, int k) { // 对从u到v的路径进行修改 
	while (node[u].top != node[v].top) { // 如果u,v不在同一条重链 
		if (node[node[u].top].dep < node[node[v].top].dep)	swap(u, v); // 确保u在跳跃到链顶时,深度大于v 
		modify(1 , 1, n, node[u].dfn, node[node[u].top].dfn, k); // 修改从u到重链链顶的值 
		u = node[node[u].top].fa; // 将u跳至其所在重链链顶的父节点 
	}
	if (node[u].dfn > node[v].dfn)	swap(u, v); // 确保时间戳u在v左侧 
	modify(1, 1, n, node[u].dfn, node[v].dfn, k); // 修改u到v的权值 
	return;
}

long long TREE_path_sum(int u, int v) { // 询问u到v路径上的权值和 
	long long ans = 0;
	while (node[u].top != node[v].top) { // 如果u,v不在同一条重链  
		if (node[node[u].top].dep < node[node[v].top].dep)	swap(u, v);// 确保u在跳跃到链顶时,深度大于v 
		ans = (ans + query(1, 1, n, node[u].dfn, node[node[u].top].dfn)) % mod; // ans加上从u到重链链顶的权值和 
		u = node[node[u].top].fa;  // 将u跳至其所在重链链顶的父节点
	}
	if (node[u].dfn > node[v].dfn)	swap(u, v); // 确保时间戳u在v左侧 
	ans = (ans + query(1, 1, n, node[u].dfn, node[v].dfn)) % mod; // ans 加上u到v的权值和 
	return ans;
}

void TREE_root_modify(int u, int k) { // 修改以u为根子树的权值 
	modify(1, 1, n, node[u].dfn, node[u].low, k);
	return;
}

long long TREE_root_sum(int u) { // 询问以u为根子树的权值和 
	return query(1, 1, n, node[u].dfn, node[u].low);
}

int main() {
	scanf("%d%d%d%d", &n, &m, &r, &mod);
	for (int i = 1; i <= n; i++)	scanf("%d", &w[i]); // 读入各点初始权值 
	for (int i = 1; i < n; i++) { 
		int x, y;
		scanf("%d%d", &x, &y);
		add(x, y), add(y, x); // 建立无向边 
	}
	
	TREE_build(r, 0); // 建立以r为根的树, r的深度为0 
	TREE_decomposition(r, r); // 以r为链顶,从r开始剖分重链 
	build(1, 1, n); // 建立线段树 
	
	
	for (int i = 1; i <= m; i++) {
		int op;
		scanf("%d", &op);
		if (op == 1) { // 修改x到y路径上节点的权值 
			int x, y, z;
			scanf("%d%d%d", &x, &y, &z);
			TREE_path_modify(x, y, z);
		}
		if (op == 2) { // 询问x到y路径上节点的权值和 
			int x, y;
			scanf("%d%d", &x, &y);
			printf("%lld\n", TREE_path_sum(x, y));
		}
		if (op == 3) { // 修改以x为根的子树的权值 
			int x, y;
			scanf("%d%d", &x, &y);
			TREE_root_modify(x, y);
		}
		if (op == 4) { // 查询以x为根的子树的权值和 
			int x;
			scanf("%d", &x);
			printf("%lld\n", TREE_root_sum(x));
		}
	}
	return 0;
}

感谢。

2020/6/18 16:05
加载中...