题目描述
分析
我们知道xor是可逆的,那么对于一个询问x,y,设lca是z,假如我们设val[i]为i到根的xor值,就可以知道val[x]^val[y]^val[father(z)]是答案嘛。
另外,xor题一般的思路是先拆位。
那么对于某个节点我们要让他整颗子树原本的0,1状态掉转(xor 1),或者不动(xor 0)。现在考虑如何放到val上来:我们发现它子树的点跟他深度奇偶性相同的点val是不变的,而奇偶性不同的都翻转了。那这拿线段树以dfn为下标就很好维护了。
代码
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
const int N=200005;
struct rec
{
int tag;
//int v;
};
char ch;
int b[N*2],next[N*2],first[N],tt;
int tr[2][N*4],fa[N][18],f[2][N],dis[N],n,m,a[N],st[N],en[N],t[2],x,y,i,j,l,dur,lc;
void cr(int x,int y)
{
tt++;
b[tt]=y;
next[tt]=first[x];
first[x]=tt;
}
void swap(int &x,int &y)
{
int z=x;
x=y;
y=z;
}
void down(int ty,int x,int l,int r)
{
if (l!=r)
{
tr[ty][x*2]^=tr[ty][x];
tr[ty][x*2+1]^=tr[ty][x];
tr[ty][x]=0;
}
}
void change(int ty,int x,int l,int r,int i,int j)
{
int m=(l+r)/2;
down(ty,x,l,r);
if (l==i&&r==j)
{
tr[ty][x]^=y;
return;
}
if (m>=j) change(ty,x*2,l,m,i,j);else
if (m<i) change(ty,x*2+1,m+1,r,i,j);else
{
change(ty,x*2,l,m,i,m);
change(ty,x*2+1,m+1,r,m+1,j);
}
}
int get(int ty,int x,int l,int r,int pos)
{
int m=(l+r)/2;
down(ty,x,l,r);
if (l==r)
return f[ty][l]^tr[ty][x];
if (m>=pos)
return get(ty,x*2,l,m,pos);
else
return get(ty,x*2+1,m+1,r,pos);
}
int lca(int x,int y)
{
if (dis[x]<dis[y]) swap(x,y);
int i;
if (dis[x]!=dis[y])
{
i=trunc(log(double(dis[x]-dis[y]))/log(2));
while (i>=0)
{
while (i>=0&&dis[fa[x][i]]<dis[y]) i--;
if (i==-1) break;
x=fa[x][i];
i--;
}
}
if (x==y) return x;
i=trunc(log(double(dis[x]))/log(2));
while (i>=0)
{
while (i>=0&&fa[x][i]==fa[y][i]) i--;
if (i==-1) break;
x=fa[x][i];
y=fa[y][i];
}
return fa[x][0];
}
void dfs(int x,int y)
{
a[x]^=a[y];
fa[x][0]=y;
dis[x]=dis[y]+1;
int tmp=dis[x]%2;
t[tmp]++;
f[tmp][t[tmp]]=a[x];
st[x]=t[tmp];
for(int p=first[x];p;p=next[p])
if (b[p]!=y)
dfs(b[p],x);
en[x]=t[tmp];
}
int F(int x)
{
if (!x) return 0;
return get(dis[x]%2,1,1,t[dis[x]%2],st[x]);
}
int main()
{
freopen("phase.in","r",stdin);
freopen("phase.out","w",stdout);
scanf("%d",&n);
fo(i,1,n-1)
{
scanf("%d%d",&x,&y);
cr(x,y);
cr(y,x);
}
fo(i,1,n) scanf("%d",a+i);
dfs(1,0);
fo(j,1,trunc(log((double)n)/log(2)))
fo(i,1,n)
fa[i][j]=fa[fa[i][j-1]][j-1];
scanf("%d\n",&m);
fo(l,1,m)
{
scanf("%c %d %d\n",&ch,&x,&y);
if (ch=='C')
change(dis[x]%2,1,1,t[dis[x]%2],st[x],en[x]);
else
{
lc=lca(x,y);
dur=F(x)^
F(y)^
F(lc)^
F(fa[lc][0]);
printf("%d\n",dur);
}
}
}
思考
虽然一般来说,直接维护某个点到根的路径的信息大部分时候不优,但也有特殊情况,这道题就是一例,做题时,我们不能轻易略过某种思路,必须思考一下,也许会有转机。