【题解】P6098 [USACO19FEB] Cow Land G

题目传送门

题意

给定一颗树,支持单点修改、树上路径异或和查询。

题解

这是一道重链剖分的裸题。

重链剖分是将每个节点的子树大小最大的儿子节点作为重儿子,其余作为轻儿子。

父节点与其重儿子的连边称为重边

然后将整棵树重新编号,把整棵树拆成许多重链(由重边相连得到的链)和 轻链(由非重边相连得到的链)。

重新编号之后,使用树状数组线段树维护即可。如果不了解重链剖分,不妨看看这篇博客

本题需要支持单点修改、树上路径异或和查询,可以使用线段树维护。

复杂度\(\mathcal{O}(q\log ^2n)\)

注意异或操作在C++中表示为^

代码

#include <cstdio>
#include <cstring>

using namespace std;

#define in __inline__
typedef long long ll;
in ll max(ll x, ll y){return x > y ? x : y;}
in ll min(ll x, ll y){return x < y ? x : y;}
in void swap(ll &x, ll &y){x ^= y ^= x ^= y;}
#define rei register int
#define FOR(i, l, r) for(rei i = l; i <= r; ++i)
#define FOL(i, r, l) for(rei i = r; i >= l; --i)
char inputbuf[1 << 23], *p1 = inputbuf, *p2 = inputbuf;
#define getchar() (p1 == p2 && (p2 = (p1 = inputbuf) + fread(inputbuf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
in ll read() {
	ll res = 0; char ch = getchar(); bool f = true;
	for(; ch < '0' || ch > '9'; ch = getchar())
		if(ch == '-') f = false;
	for(; ch >= '0' && ch <= '9'; ch = getchar())
		res = res * 10 + (ch ^ 48);
	return f ? res : -res;
}
const int N = 1e5 + 15;

int a[N], v[N], sum[N << 2], fr[N << 1], to[N << 1], nxt[N << 1], head[N], cnt;
int dep[N], id[N], top[N], fa[N], siz[N], son[N], tot, n, x, y;
/*
dep[i]:节点i的深度
id[i]:重新编号后节点i的编号
top[i]:包括节点i的重链顶端
siz[i]:以i为根的子树大小
son[i]:节点i的重儿子
fa[i]:节点i的父亲
v[i]:重新编号后编号为i的节点的值
*/

in void add(int x, int y) {to[++cnt] = y, nxt[cnt] = head[x], head[x] = cnt;}
in void upd(int k) {sum[k] = sum[k << 1] ^ sum[k << 1 | 1];}

void dfs1(int u, int f) {//处理各种信息,包括深度、父节点、子树大小、重儿子
	int v; fa[u] = f, dep[u] = dep[f] + 1, siz[u] = 1;
	for(rei i = head[u]; i; i = nxt[i]) if((v = to[i]) ^ f) {
		dfs1(v, u); siz[u] += siz[v]; if(siz[v] > siz[son[u]]) son[u] = v;
	}
}
void dfs2(int u, int tf) {//重新编号
	id[u] = ++tot, v[tot] = a[u]; top[u] = tf;
	if(!son[u]) return; dfs2(son[u], tf);
	for(rei i = head[u]; i; i = nxt[i]) if(!id[to[i]]) dfs2(to[i], to[i]);
}

void bd(int k, int l, int r) {
	if(l == r) {sum[k] = v[l]; return;}
	int m = l + r >> 1;
	bd(k << 1, l, m); bd(k << 1 | 1, m + 1, r); upd(k);
}
void cg(int k, int l, int r) {
	if(l == r) {sum[k] = y; return;}
	int m = l + r >> 1;
	id[x] <= m ? cg(k << 1, l, m) : cg(k << 1 | 1, m + 1, r); upd(k);
}
int qr(int k, int l, int r, int x, int y) {
	if(x <= l && r <= y) return sum[k];
	int m = l + r >> 1, res = 0;
	if(x <= m) res ^= qr(k << 1, l, m, x, y);
	if(m < y) res ^= qr(k << 1 | 1, m + 1, r, x, y);
	return res;
}
int query(int x, int y, int res = 0) {//树上路径查询
	for(; top[x] ^ top[y]; y = fa[top[y]]) {
		if(dep[top[x]] > dep[top[y]]) x ^= y ^= x ^= y;
		res ^= qr(1, 1, n, id[top[y]], id[y]);
	}
	if(id[x] > id[y]) x ^= y ^= x ^= y;
	return (res ^= qr(1, 1, n, id[x], id[y]));
}

signed main() {
	int q, opt;
	n = read(); q = read();
	FOR(i, 1, n) a[i] = read();
	FOR(i, 1, n - 1) x = read(), y = read(), add(x, y), add(y, x);
	dfs1(1, 0);
	dfs2(1, 1);
	bd(1, 1, n);
	for(; q; --q) {
		opt = read(), x = read(), y = read();
		if(opt == 1) cg(1, 1, n);
		else printf("%d\n", query(x, y));
	}
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/creating-2007/p/13196541.html