BZOJ - 2819 Nim
题意:
给出一棵树。两种操作,第一种是询问点u,v路径上的异或和,另一种是改一个点的值。
题解:
维护每个点到根节点的异或和。那么两个点之间的异或和就是两个点分别到根节点的异或和相异或,再异或上他们LCA节点的权值。
对于每次更改,只会影响他的子树到根节点的异或和。所以可以维护DFS序,每次修改一个点的值时,用树状数组差分的形式修改in[]和out[]区间内的点。
#include <bits/stdc++.h> using namespace std; const int N = 5e5+10; char s[2]; int n, q; int u, v, tot, cnt; int w[N]; int tre[N]; int in[N], out[N]; int depth[N]; int fa[20][N]; int head[N], to[N<<1], nxt[N<<1]; void add(int pos, int v) { while(pos <= n) { tre[pos] ^= v; pos += pos&(-pos); } } int sum(int pos) { int res = 0; while(pos > 0) { res ^= tre[pos]; pos -= pos&(-pos); } return res; } void dfs(int u, int pre, int d) { in[u] = ++cnt; depth[u] = d; fa[0][u] = pre; add(cnt, w[u]); for(int i = head[u]; ~i; i = nxt[i]) { if(to[i] == pre) continue; dfs(to[i], u, d+1); } out[u] = cnt; add(cnt+1, w[u]); } int lca(int u, int v) { if(depth[u] < depth[v]) swap(u, v); int state = depth[u]-depth[v]; for(int i = 19; i >= 0; i--) if((1<<i) & state) { u = fa[i][u]; } if(u == v) return u; for(int i = 19; i >= 0; i--) if(fa[i][u] != fa[i][v]) { u = fa[i][u]; v = fa[i][v]; } return fa[0][u]; } int main() { scanf("%d", &n); memset(head, -1, sizeof(int)*(n+2)); for(int i = 1; i <= n; i++) scanf("%d", &w[i]); for(int i = 1; i < n; i++) { scanf("%d%d", &u, &v); to[++tot] = v; nxt[tot] = head[u]; head[u] = tot; to[++tot] = u; nxt[tot] = head[v]; head[v] = tot; } dfs(1, 0, 0); for(int i = 0; i < 19; i++) for(int j = 1; j <= n; j++) fa[i+1][j] = fa[i][fa[i][j]]; scanf("%d", &q); while(q--) { scanf("%s%d%d", s, &u, &v); if(s[0] == 'Q') { if(sum(in[u])^sum(in[v])^w[lca(u, v)]) puts("Yes"); else puts("No"); } else { add(in[u], w[u]^v); add(out[u]+1, w[u]^v); w[u] = v; } } }