[BZOJ2819]Nim
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
其实就是个树剖板子题。。。只要会博弈论知识就好。。。不会博弈论可以参考这篇博客
/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline char gc(){
static char buf[1000000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int frd(){
int x=0,f=1;char ch=gc();
for (;ch<'0'||ch>'9';ch=gc()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=gc()) x=(x<<1)+(x<<3)+ch-'0';
return x*f;
}
inline int read(){
int x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return x*f;
}
inline void print(int x){
if (x<0) putchar('-'),x=-x;
if (x>9) print(x/10);
putchar(x%10+'0');
}
const int N=5e5;
int dfn[N+10],ID[N+10],v[N+10],n;
struct S1{
#define ls (p<<1)
#define rs (p<<1|1)
int tree[(N<<2)+10];
void build(int p,int l,int r){
if (l==r){
tree[p]=v[dfn[l]];
return;
}
int mid=(l+r)>>1;
build(ls,l,mid),build(rs,mid+1,r);
tree[p]=tree[ls]^tree[rs];
}
void Modify(int p,int l,int r,int x,int v){
if (l==r){
tree[p]=v;
return;
}
int mid=(l+r)>>1;
if (x<=mid) Modify(ls,l,mid,x,v);
else Modify(rs,mid+1,r,x,v);
tree[p]=tree[ls]^tree[rs];
}
int Query(int p,int l,int r,int x,int y){
if (x<=l&&r<=y) return tree[p];
int mid=(l+r)>>1,res=0;
if (x<=mid) res^=Query(ls,l,mid,x,y);
if (y>mid) res^=Query(rs,mid+1,r,x,y);
return res;
}
}ST;//Segment Tree;
struct S2{
int pre[(N<<1)+10],now[N+10],child[(N<<1)+10],tot,Time;
int top[N+10],Rem[N+10],size[N+10],deep[N+10],fa[N+10];
void join(int x,int y){pre[++tot]=now[x],now[x]=tot,child[tot]=y;}
void insert(int x,int y){join(x,y),join(y,x);}
void dfs(int x){
deep[x]=deep[fa[x]]+1,size[x]=1;
for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
if (son==fa[x]) continue;
fa[son]=x,dfs(son);
size[x]+=size[son];
if (size[Rem[x]]<size[son]) Rem[x]=son;
}
}
void build(int x){
if (!x) return;
top[x]=Rem[fa[x]]==x?top[fa[x]]:x;
dfn[ID[x]=++Time]=x;
build(Rem[x]);
for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
if (son==fa[x]||son==Rem[x]) continue;
build(son);
}
}
int solve(int x,int y){
int res=0;
while (top[x]!=top[y]){
if (deep[top[x]]<deep[top[y]]) swap(x,y);
res^=ST.Query(1,1,n,ID[top[x]],ID[x]);
x=fa[top[x]];
}
if (deep[x]>deep[y]) swap(x,y);
res^=ST.Query(1,1,n,ID[x],ID[y]);
return res;
}
}HLD;//Heavy Light Decomposition
int main(){
n=read();
for (int i=1;i<=n;i++) v[i]=read();
for (int i=1;i<n;i++){
int x=read(),y=read();
HLD.insert(x,y);
}
HLD.dfs(1),HLD.build(1),ST.build(1,1,n);
int m=read();
for (int i=1;i<=m;i++){
char ch[2];
scanf("%s",ch);
int x=read(),y=read();
if (ch[0]=='Q') printf(HLD.solve(x,y)?"Yes\n":"No\n");
if (ch[0]=='C') ST.Modify(1,1,n,ID[x],y);
}
return 0;
}