【题解】
题目大意:
给定一棵树,有两种操作:
1. 修改一个节点的权值
2. 查询两点之间路径上所有点的权值异或值
运用前缀的思想
首先xor运算有一个性质:Xor[l,r]=Xor[1,l]^Xor[1,r]
所以,设 Xor[x]为从结点x到根经过的所有结点的权值异或,
则 x到y路径上的点权异或值为:Xor[x] ^ Xor[y] ^ Val[LCA(x,y)]
修改一个点x,会改变以它为根的子树中所有节点的Xor[]值
求出树上所有节点的dfs序,这个修改就变成了连续区间的修改
对单点的查询,线段树可以做到 O(logN),
今天又学到了一种新方法:利用前缀和打标记的思想,若将区间[l,r]内的元素全部异或x,相当于在第l位标记x,再在第r+1位标记x,
这样,对于第r位以后的元素,这两个命令互相抵消,查询某个元素的值,只需用树状数组把它之前的命令全部累加起来即可
另外,关于dfs序,一个结点的r值可以等于它最后一个孩子的r值,不必加1,这样,序列的数目还是N,而不是2*N
RE后省掉了原来的Xor数组,用初始的打标记代替了,然后果断就AC了。。。
【代码】
#include<stdio.h> #include<stdlib.h> int a[500005],l[500005],r[500005],f[25][500005],deep[500005],v[1000005],first[500005],next[1000005],c[500005]; int n,e=0,pos=0; void tj(int x,int y) { v[++e]=y; next[e]=first[x]; first[x]=e; } void dfs(int x,int fa) { int i; l[x]=++pos; f[0][x]=fa; deep[x]=deep[fa]+1; for(i=first[x];i!=0;i=next[i]) if(v[i]!=fa) dfs(v[i],x); r[x]=pos; } int LCA(int x,int y) { int i,j,t; if(deep[x]>deep[y]) { t=x; x=y; y=t; } while(deep[x]<deep[y]) { for(i=0;deep[x]<=deep[y]-(1<<i);i++); y=f[i-1][y]; } while(x!=y) { for(i=1;f[i][x]!=f[i][y];i++); x=f[i-1][x]; y=f[i-1][y]; } return x; } void xg(int p,int i) { for(;i<=n;i+=i&(-i)) c[i]^=p; } int cx(int i) { int ans=0; for(;i>0;i-=i&(-i)) ans^=c[i]; return ans; } int main() { char opt; int q,i,j,x,y; scanf("%d",&n); for(i=1;i<=n;i++) scanf("%d",&a[i]); for(i=1;i<n;i++) { scanf("%d%d",&x,&y); tj(x,y); tj(y,x); } dfs(1,0); for(i=1;i<=20;i++) for(j=1;j<=n;j++) f[i][j]=f[i-1][f[i-1][j]]; for(i=1;i<=n;i++) { xg(a[i],l[i]); xg(a[i],r[i]+1); } scanf("%d",&q); for(;q>0;q--) { scanf("\n%c %d%d",&opt,&x,&y); if(opt=='C') { xg(a[x]^y,l[x]); if(r[x]<n) xg(a[x]^y,r[x]+1); a[x]=y; } else { if( cx(l[x])^cx(l[y])^a[LCA(x,y)] ) printf("Yes\n"); else printf("No\n"); } } return 0; }