2819: Nim
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 2787 Solved: 1040
[ Submit ][ Status ][ Discuss ]
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
No
Yes
Yes
Yes
果断链剖
线段树维护疑惑和即可
只有单点修改 还比较好打
查询写错 暴调两小时 笑哭
#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<complex>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<map>
#include<set>
using namespace std;
typedef double db;
typedef long long ll;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){x=10*x+ch-'0';ch=getchar();}
return x*f;
}
inline void print(int x)
{if(x<0)putchar('-');if(x>=10)print(x/10);putchar(x%10+'0');}
const int N=500100;
int n,ecnt,last[N],a[N];
struct EDGE{int to,nt;}e[N<<1];
inline void add(int u,int v)
{e[++ecnt]=(EDGE){v,last[u]};last[u]=ecnt;}
int fa[N],size[N],dfn[N],top[N],dep[N],pos[N],cnt;
void dfs1(int u)
{
size[u]=1;
for(int i=last[u];i;i=e[i].nt)
{
if(fa[u]==e[i].to)continue;
dep[e[i].to]=dep[u]+1;fa[e[i].to]=u;
dfs1(e[i].to);size[u]+=size[e[i].to];
}
}
void dfs2(int u,int tp)
{
top[u]=tp;int k=0;dfn[u]=++cnt;pos[cnt]=u;
for(int i=last[u];i;i=e[i].nt)
{
if(fa[u]==e[i].to)continue;
if(size[e[i].to]>size[k])k=e[i].to;
}
if(!k)return ;
dfs2(k,tp);
for(int i=last[u];i;i=e[i].nt)
{
if(fa[u]==e[i].to||e[i].to==k)continue;
dfs2(e[i].to,e[i].to);
}
}
struct segtree{int l,r,xsum;}tr[N*5];
inline void pushup(int k)
{tr[k].xsum=tr[k<<1].xsum^tr[k<<1|1].xsum;}
void build(int l,int r,int k)
{
tr[k].l=l;tr[k].r=r;
if(l==r){tr[k].xsum=a[pos[l]];return ;}
int mid=l+r>>1;
build(l,mid,k<<1);build(mid+1,r,k<<1|1);
pushup(k);
}
void modify(int k,int x,int val)
{
int l=tr[k].l,r=tr[k].r;
if(l==r){tr[k].xsum^=val;return ;}
int mid=l+r>>1;
if(x<=mid)modify(k<<1,x,val);
else modify(k<<1|1,x,val);
pushup(k);
}
int seg_xsum(int k,int x,int y)
{
int l=tr[k].l,r=tr[k].r;
if(l>=x&&r<=y)return tr[k].xsum;
int mid=l+r>>1;
if(mid>=y)return seg_xsum(k<<1,x,y);
else if(mid<x)return seg_xsum(k<<1|1,x,y);
else return seg_xsum(k<<1|1,x,y)^seg_xsum(k<<1,x,y);
}
inline void query(int u,int v)
{
int ans=0;
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]])swap(u,v);
ans^=seg_xsum(1,dfn[top[u]],dfn[u]);u=fa[top[u]];
}
if(dfn[u]<dfn[v])swap(u,v);
ans^=seg_xsum(1,dfn[v],dfn[u]);
if(ans)puts("Yes");else puts("No");
}
int main()
{
n=read();
for(int i=1;i<=n;++i)a[i]=read();
for(int i=1,u,v;i<n;++i)
{u=read();v=read();add(u,v);add(v,u);}
dfs1(1);dfs2(1,1);build(1,n,1);
int m=read();
while(m--)
{
char ch[2];scanf("%s",ch);int x=read(),y=read();
if(ch[0]=='C'){modify(1,dfn[x],y^a[x]);a[x]=y;}
else query(x,y);
}
return 0;
}
/*
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
Yes
No
Yes
Yes
Yes
*/