【题解】
题目大意:
给定一棵树,有两种操作:
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;
}