Description
著名游戏设计师vfleaking,最近迷上了Nim。普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取。谁不能取谁输。这个游戏是有必胜策略的。于是vfleaking决定写一个玩Nim游戏的平台来坑玩家。
为了设计漂亮一点的初始局面,vfleaking用以下方式来找灵感:拿出很多石子,把它们聚成一堆一堆的,对每一堆编号1,2,3,4,...n,在堆与堆间连边,没有自环与重边,从任意堆到任意堆都只有唯一一条路径可到达。然后他不停地进行如下操作:
1.随机选两个堆v,u,询问若在v到u间的路径上的石子堆中玩Nim游戏,是否有必胜策略,如果有,vfleaking将会考虑将这些石子堆作为初始局面之一,用来坑玩家。
2.把堆v中的石子数变为k。
由于vfleaking太懒了,他懒得自己动手了。请写个程序帮帮他吧。
Input
第一行一个数n,表示有多少堆石子。
接下来的一行,第i个数表示第i堆里有多少石子。
接下来n-1行,每行两个数v,u,代表v,u间有一条边直接相连。
接下来一个数q,代表操作的个数。
接下来q行,每行开始有一个字符:
如果是Q,那么后面有两个数v,u,询问若在v到u间的路径上的石子堆中玩Nim游戏,是否有必胜策略。
如果是C,那么后面有两个数v,k,代表把堆v中的石子数变为k。
对于100%的数据:
1≤N≤500000, 1≤Q≤500000, 0≤任何时候每堆石子的个数≤32767
其中有30%的数据:
石子堆组成了一条链,这3个点会导致你DFS时爆栈(也许你不用DFS?)。其它的数据DFS目测不会爆。
注意:石子数的范围是0到INT_MAX
Output
对于每个Q,输出一行Yes或No,代表对询问的回答。
Sample Input
【样例输入】
5
1 3 5 2 5
1 5
3 5
2 5
1 4
6
Q 1 2
Q 3 5
C 3 7
Q 1 2
Q 2 4
Q 5 3
Sample Output
Yes
No
Yes
Yes
Yes
题目分析
首先 Nim先手必赢得条件是所有石子数异或和不为0
那么题目要求 单点修改 询问两点路径异或和
这明显是树剖嘛QAQ 但是出题人告诉我们爆栈
那...写广搜树剖嘛
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
using namespace std;
int n,m;
char s[10];
int val[500010];
int head[500010],to[500010*2],net[500010*2];
int deep[500010],size[500010],number[500010],son[500010],top[500010],fa[500010],value[500010];
int tot,cnt,que[500100];
int read()
{
char ch=getchar();
int sum=0;
while(!(ch>='0'&&ch<='9')) ch=getchar();
while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=getchar();
return sum;
}
void add(int x,int y)
{
net[++tot]=head[x],head[x]=tot,to[tot]=y;
}
void bfs()
{
int l=0,r=0;
que[++r]=1;
while(l!=r)
{
int x=que[++l];
deep[x]=deep[fa[x]]+1;
size[x]=1;
for(int i=head[x];i;i=net[i])
if(to[i]!=fa[x]) fa[to[i]]=x,que[++r]=to[i];
}
for(int i=n;i>=1;i--)
{
int x=que[i];
size[fa[x]]+=size[x];
if(size[x]>size[son[fa[x]]]) son[fa[x]]=x;
}
for(int i=1;i<=n;i++)
{
int x=que[i];
if(son[fa[x]]==x) top[x]=top[fa[x]];
else
{
top[x]=x;
for(int j=x;j;j=son[j]) number[j]=++cnt,value[cnt]=val[j];
}
}
}
struct your
{
int x,y,sum;
}a[500010*4];
void build(int dx,int dy,int num)
{
a[num].x=dx,a[num].y=dy;
if(dy==dx)
{
a[num].sum=value[dx];
return ;
}
int mid=(dx+dy)>>1;
build(dx,mid,num<<1),build(mid+1,dy,num<<1|1);
a[num].sum=a[num<<1].sum^a[num<<1|1].sum;
}
void update(int dx,int c,int num)
{
if(dx==a[num].x&&dx==a[num].y)
{
a[num].sum=c;
return ;
}
int mid=(a[num].x+a[num].y)>>1;
if(dx<=mid) update(dx,c,num<<1);
else update(dx,c,num<<1|1);
a[num].sum=a[num<<1].sum^a[num<<1|1].sum;
}
int ask(int dx,int dy,int num)
{
if(a[num].x==dx&&a[num].y==dy) return a[num].sum;
int mid=(a[num].x+a[num].y)>>1;
if(dx>mid) return ask(dx,dy,num<<1|1);
else if(dy<=mid) return ask(dx,dy,num<<1);
else return ask(dx,mid,num<<1)^ask(mid+1,dy,num<<1|1);
}
int find(int x,int y)
{
int sum=0;
while(top[x]!=top[y])
{
if(deep[top[x]]<deep[top[y]]) swap(x,y);
sum^=ask(number[top[x]],number[x],1);
x=fa[top[x]];
}
if(deep[x]>deep[y]) swap(x,y);
sum^=ask(number[x],number[y],1);
return sum;
}
int main()
{
n=read();
for(int i=1;i<=n;i++) val[i]=read();
for(int i=1;i<n;i++)
{
int x,y;
x=read(),y=read();
add(x,y),add(y,x);
}
bfs();
build(1,n,1);
m=read();
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%s",&s[0]);
x=read(),y=read();
if(s[0]=='C') update(number[x],y,1);
else printf("%s\n",find(x,y)==0?"No":"Yes");
}
return 0;
}