Description
著名游戏设计师vfleaking,最近迷上了Nim。普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取。谁不能取谁输。这个游戏是有必胜策略的。于是vfleaking决定写一个玩Nim游戏的平台来坑玩家。
为了设计漂亮一点的初始局面,vfleaking用以下方式来找灵感:拿出很多石子,把它们聚成一堆一堆的,对每一堆编号1,2,3,4,…n,在堆与堆间连边,没有自环与重边,从任意堆到任意堆都只有唯一一条路径可到达。然后他不停地进行如下操作:
1.随机选两个堆v,u,询问若在v到u间的路径上的石子堆中玩Nim游戏,是否有必胜策略,如果有,vfleaking将会考虑将这些石子堆作为初始局面之一,用来坑玩家。
2.把堆v中的石子数变为k。
由于vfleaking太懒了,他懒得自己动手了。请写个程序帮帮他吧。
Solution
首先这个sg函数并没有什么用因为我们可以打表发现本题中的sg[x]=x
其次我在阅读题目的时候注意到了爆栈的字眼因此非常保守地写了bfs求dfs序,但是鱿鱼种种原因这题直接dfs是可行的
那么这就是一个求树上路径异或和是否为0+动态修改的问题,一般套路是树剖,然而两个log的复杂度并不是这题能承受的
一个直观的想法是记b[i]为点i到根路径上的异或和,而修改点x只影响x所在子树中的节点,并且这些节点一定是dfs序连续的一段。这个可以用树状数组搞搞
然而上面这个比较直观的方法并没有获得评测姬的认可,因此考虑另一种更加显然的思路。我比较着急去吃宵夜这里就不写了罢
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define fill(x,t) memset(x,t,sizeof(x))
#define lowbit(x) ((x)&(-(x)))
const int N=500005;
const int E=1000005;
struct edge {int y,next;} e[E];
int ls[N],edCnt;
int a[N],b[N],c[N],fa[N][21],n,T;
int queue[N],size[N];
int dep[N],pos[N];
int read() {
int x=0,v=1; char ch=getchar();
for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
return x*v;
}
void add_edge(int x,int y) {
e[++edCnt]=(edge) {y,ls[x]}; ls[x]=edCnt;
e[++edCnt]=(edge) {x,ls[y]}; ls[y]=edCnt;
}
void add(int x,int v) {
for (;x<=n;x+=lowbit(x)) c[x]^=v;
}
int get(int x) {
int ret=0;
for (;x;x-=lowbit(x)) ret^=c[x];
return ret;
}
void bfs(int st) {
dep[st]=1; b[st]=a[st];
int head=1,tail=0; queue[++tail]=st;
while (head<=tail) {
int now=queue[head++]; size[now]=1;
rep(i,1,20) fa[now][i]=fa[fa[now][i-1]][i-1];
for (int i=ls[now];i;i=e[i].next) {
if (dep[e[i].y]) continue;
dep[e[i].y]=dep[fa[e[i].y][0]=now]+1;
queue[++tail]=e[i].y;
b[e[i].y]=a[e[i].y]^b[now];
}
}
drp(i,n,1) size[fa[queue[i]][0]]+=size[queue[i]];
int last=(pos[1]=1);
rep(i,2,n) {
if (fa[queue[i]][0]!=fa[queue[i-1]][0]) last=pos[fa[queue[i]][0]];
else last+=size[queue[i-1]];
pos[queue[i]]=last+1;
}
}
int get_lca(int x,int y) {
if (dep[x]<=dep[y]) std:: swap(x,y);
drp(i,20,0) if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
if (x==y) return x;
drp(i,20,0) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int main(void) {
// freopen("data.in","r",stdin);
n=read();
rep(i,1,n) a[i]=read();
rep(i,2,n) add_edge(read(),read());
bfs(1);
// rep(i,1,n) add(pos[i],b[i]);
rep(i,1,n) {
add(pos[i],a[i]);
add(pos[i]+size[i],a[i]);
}
T=read();
while (T--) {
char opt[2]; scanf("%s",opt);
int x=read(),y=read();
if (opt[0]=='Q') {
int lca=get_lca(x,y);
// int qx=get(pos[x])^get(pos[x]-1);
// int qy=get(pos[y])^get(pos[y]-1);
int qx=get(pos[x]);
int qy=get(pos[y]);
int ans=qx^qy^a[lca];
if (ans) puts("Yes");
else puts("No");
} else {
add(pos[x],a[x]^y);
add(pos[x]+size[x],a[x]^y);
a[x]=y;
}
}
return 0;
}