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
解法:裸树剖,网上说要人工栈,我直接递归剖分过了啊。。不会人工栈,但是卡栈的可能性很小?
///BZOJ 2819 #include <bits/stdc++.h>
using namespace std;
const int maxn = 1000010;
struct edge{int v,next;}E[maxn*2];
struct seg{int l,r,Xor;}tree[maxn*4];
int n,m,tot,edgecnt,tim, root;
int num[maxn],siz[maxn],top[maxn],son[maxn],dep[maxn],tid[maxn],fa[maxn],head[maxn];
void init(){
memset(head,-1,sizeof(head));
memset(son,-1,sizeof(son));
edgecnt=tim=0;
}
void addedge(int u, int v){
E[edgecnt].v=v,E[edgecnt].next=head[u],head[u]=edgecnt++;
}
void dfs1(int u, int father, int d){
dep[u]=d;
fa[u]=father;
siz[u]=1;
for(int i=head[u];~i;i=E[i].next){
int v=E[i].v;
if(v==father) continue;
dfs1(v,u,d+1);
siz[u]+=siz[v];
if(son[u]==-1||siz[v]>siz[son[u]]) son[u]=v;
}
}
void dfs2(int u, int tp){
top[u]=tp;
tid[u]=++tim;
if(son[u]==-1) return;
dfs2(son[u],tp);
for(int i=head[u];~i;i=E[i].next){
int v=E[i].v;
if(v!=son[u]&&v!=fa[u]) dfs2(v,v);
}
}
void pushup(int rt){
tree[rt].Xor=tree[rt*2].Xor^tree[rt*2+1].Xor;
}
void build(int l, int r, int rt){
tree[rt].l=l,tree[rt].r=r,tree[rt].Xor=0;
if(l==r) return;
int mid=(l+r)/2;
build(l,mid,rt*2);
build(mid+1,r,rt*2+1);
}
void update(int pos, int v, int rt){
if(tree[rt].l==tree[rt].r){
tree[rt].Xor=v;
return;
}
int mid=(tree[rt].l+tree[rt].r)/2;
if(pos<=mid) update(pos,v,rt*2);
else update(pos,v,rt*2+1);
pushup(rt);
}
int query(int L, int R, int rt){
if(L==tree[rt].l&&tree[rt].r==R){
return tree[rt].Xor;
}
int mid=(tree[rt].l+tree[rt].r)/2;
if(R<=mid) return query(L,R,rt*2);
else if(L>mid) return query(L,R,rt*2+1);
else return query(L,mid,rt*2)^query(mid+1,R,rt*2+1);
}
int solve(int x, int y){
int ans = 0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
ans^=query(tid[top[x]],tid[x],1);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
ans^=query(tid[x],tid[y],1);
return ans;
}
int main(){
scanf("%d", &n);
init();
for(int i=1; i<=n; i++) scanf("%d", &num[i]);
for(int i=1; i<n; i++){
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y);
addedge(y,x);
}
dfs1(1,1,1);
dfs2(1,1);
build(1,n,1);
for(int i=1; i<=n; i++) update(tid[i],num[i],1);
scanf("%d", &m);
while(m--){
char op[3];
scanf("%s", op);
int x, y;
if(op[0]=='Q'){
scanf("%d%d", &x,&y);
int ans=solve(x,y);
if(!ans) puts("No");
else puts("Yes");
}
else{
scanf("%d%d",&x,&y);
update(tid[x],y,1);
}
}
return 0;
}