#213-[树链剖分]树(GDKOI2015)

Description

给定一棵树,树节点编号为 0~N-1 的,根的节点编号为 0,每个节点有初始颜色 c 和权值 v,现在有三种操作:
1.Change u x y,表示将 u 的子树中颜色为 x 和 y 的节点颜色反转,即将 x 颜色的的节点换成 y 颜色,将 y 颜色的节点换成 x 颜色。数据保证 x 与 y 不相等。
2.Ask u v c,表示询问 u 到 v 的路径上,颜色为 c 的节点的权值之和。
3.Set u c val,表示将节点 u 的颜色设置为 c,权值设置为 val。

 

Input

第一行有一个整数 N,表示树的节点数。
第二行有 N 个整数 c_i,表示每个节点的颜色。
第三行有 N 个整数 v_i,表示每个节点的权值。
接下来 N-1 行,每行两个整数 x 和 y,表示 x 和 y 有一条直接连边。
接下来一行有一个整数 M,表示操作数。
接下来 M 行,每行输入格式参照问题描述。

 

Output

对于每一次询问,输出一行。

样例1
3
0 1 2
0 1 2
0 1
0 2
4
Ask 1 2 0
Change 0 1 2
Set 0 1 98
Ask 1 2 1

样例2
3
0 1 2
0 1 2
0 1
0 2
4
Ask 1 2 1
Change 0 1 2
Set 0 1 98
Ask 1 2 2
  • Sample Input

样例1
0
100

样例2
1
1
  • Sample Output

HINT

对于 20%数据,N<=1000 M<=1000
对于 50%数据,N<=10000 M<=10000 0<=c_i<2 0<=xy<2 0<=c<2
对于 100%数据,N<=50000 M<=50000 0<=c_i<10 |v_i|<=10000 0<=uv
 

Source/Category

GDKOI2015 

直接一看,树链剖分板子,但是有些很烦人的细节.

#include 
#include 
#include 

using namespace std;
const int MAXN = 50010;
typedef long long ll;

struct edge { // 定义一个边
	int v, nxt;
} ed[MAXN<<1];
int n, depth[MAXN], dfn[MAXN], revdfn[MAXN], top[MAXN], son[MAXN], tsize[MAXN], fa[MAXN], dfstime; // 树链剖分的一大堆东西
ll cnt[MAXN<<2][12]; pair tag[MAXN<<2]; // 线段树的一大堆东西
int c[MAXN], head[MAXN], id[MAXN<<2], ed_cnt=0; // 建图要的一大堆东西
ll w[MAXN];
/*int l[MAXN<<2], r[MAXN<<2];*/

void addedge(int u, int v) { // 加边
	ed[++ed_cnt].v = v; ed[ed_cnt].nxt = head[u]; head[u] = ed_cnt;
	ed[++ed_cnt].v = u; ed[ed_cnt].nxt = head[v]; head[v] = ed_cnt;
}
void dfs(int u, int pa, int dep) { // 树链剖分的第一次深搜
	fa[u] = pa; tsize[u] = 1; son[u] = 0; depth[u] = dep; // 获得祖先深度,初始化大小重儿子
	for (int i=head[u]; i; i=ed[i].nxt) {
		int v = ed[i].v; if (v==pa) continue; // 确保不会跳回祖先
		dfs(v, u, dep+1); tsize[u] += tsize[v]; // 更新字树大小
		if (tsize[v]>tsize[son[u]]) son[u] = v; // 更新重儿子
	}
}
void dfs2(int u, int tp) { // 树链剖分的第二次深搜
	top[u] = tp; dfn[u] = ++dfstime; revdfn[dfstime] = u; // 获得重链顶时间戳
	if (son[u]) { // 如果有儿子
		dfs2(son[u], tp); // 先递归重儿子
		for (int i=head[u]; i; i=ed[i].nxt) { // 枚举轻儿子递归
			int v = ed[i].v; if ((v==fa[u]) || (v==son[u])) continue;
			dfs2(v, v);
		}
	}
}
void build(int rt, int l, int r) { // 线段树构建
	// ::l[rt] = l; ::r[rt] = r;
	tag[rt] = (pair){-1,-1}; // 初始化懒标记
	if (l==r) { // 到达边界
		cnt[rt][c[revdfn[l]]] = w[revdfn[l]]; id[rt] = revdfn[l]; return; // 初始化
	}
	int m = (l+r)>>1;
	build(rt<<1, l, m); build(rt<<1|1, m+1, r);
	for (int i=0; i<10; ++i) cnt[rt][i] = cnt[rt<<1][i]+cnt[rt<<1|1][i];
}
void pushdown(int rt, int l, int r) { // 烦人的下传懒标记
	if (tag[rt].first!=-1) { // 如果有懒标记
		if (l> 1;
			pushdown(rt<<1, l, m); pushdown(rt<<1|1, m+1, r); // 左右下传,防止懒标记被覆盖
			int x = tag[rt].first, y = tag[rt].second;
			swap(cnt[rt<<1][x], cnt[rt<<1][y]); // 交换和
			tag[rt<<1] = tag[rt]; // 下传
			if (id[rt<<1]) { // 改变颜色数组
				if (c[id[rt<<1]]==x) c[id[rt<<1]] = y;
				else if (c[id[rt<<1]]==y) c[id[rt<<1]] = x;
			}
			swap(cnt[rt<<1|1][x], cnt[rt<<1|1][y]); // 右儿子的同上
			tag[rt<<1|1] = tag[rt];
			if (id[rt<<1|1]) {
				if (c[id[rt<<1|1]]==x) c[id[rt<<1|1]] = y;
				else if (c[id[rt<<1|1]]==y) c[id[rt<<1|1]] = x;
			}
		}
		tag[rt] = (pair){-1,-1}; // 清空懒标记
	}
}
void update_swap(int rt, int l, int r, int x, int y, int k, int k2) { // 交换
	pushdown(rt, l, r);
	if ((x<=l) && (r<=y)) { // 完全包括,设懒标记
		swap(cnt[rt][k], cnt[rt][k2]); tag[rt] = (pair){k,k2}; return;
	}
	int m = (l+r) >> 1;
	if (x<=m) update_swap(rt<<1, l, m, x, y, k, k2);
	if (y>m) update_swap(rt<<1|1, m+1, r, x, y, k, k2);
	for (int i=0; i<10; ++i) cnt[rt][i] = cnt[rt<<1][i]+cnt[rt<<1|1][i]; // 上传
}
void update(int rt, int l, int r, int x, int cc, ll val) { // 更新单点信息
	pushdown(rt, l, r);
	if (l==r) {
		cnt[rt][c[revdfn[l]]] = 0LL; c[revdfn[l]] = cc;
		cnt[rt][cc] = val; return;
	}
	int m = (l+r) >> 1;
	if (x<=m) update(rt<<1, l, m, x, cc, val);
	else update(rt<<1|1, m+1, r, x, cc, val);
	for (int i=0; i<10; ++i) cnt[rt][i] = cnt[rt<<1][i]+cnt[rt<<1|1][i]; // 上传
}
ll query(int rt, int l, int r, int x, int y, int cc) { // 查询
	pushdown(rt, l, r);
	if ((x<=l) && (r<=y)) return cnt[rt][cc];
	ll res = 0LL;
	int m = (l+r) >> 1;
	if (x<=m) res += query(rt<<1, l, m, x, y, cc);
	if (y>m) res += query(rt<<1|1, m+1, r, x, y, cc);
	return res;
}
void Change(int u, int x, int y) { // 子树交换,更新子树
	if (x==y) return;
	update_swap(1, 1, n, dfn[u], dfn[u]+tsize[u]-1, x, y);
	// printf("#Swap l=%d r=%d x=%d y=%d\n", dfn[u], dfn[u]+tsize[u]-1, x, y);
}
ll Ask(int u, int v, int c) { // 路径查询
	ll res = 0LL;
	while (top[u]!=top[v]) {
		if (depth[top[u]]depth[v]) swap(u, v);
	// printf("#Ask l=%d r=%d c=%d\n", dfn[u], dfn[v], c);
	return res+query(1, 1, n, dfn[u], dfn[v], c);
}
void Set(int u, int c, ll val) { // 单点修改
	update(1, 1, n, dfn[u], c, val);
	// printf("#Set u=%d c=%d val=%lld\n", dfn[u], c, val);
}
/*
void print() { // 调试
	printf("----------");
	for (int i=1; i

 

你可能感兴趣的:(刷题)