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太懒了,他懒得自己动手了。请写个程序帮帮他吧。

 

 第一行一个数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

对于每个Q,输出一行Yes或No,代表对询问的回答。

[样例输入]
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


关于Nim游戏,可以百度一下

当所有堆异或起来为0时,必输,否则有必胜方法

于是树剖,维护区间异或的值就可以了


//没有评测但对拍过的代码
#include<bits/stdc++.h>
#define LL long long
#define N 500005
using namespace std;
int first[N],next[N*2],to[N*2],tot;
int n,q,a[N]; char s[5];
int fa[N],dep[N],size[N];
int top[N],son[N],id[N],pre[N],sign;
struct Node{int l,r,val;}t[N<<2];
int read(){
	int cnt=0,f=1;char ch=0;
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch))cnt=cnt*10+(ch-'0'),ch=getchar();
	return cnt*f;
}
void add(int x,int y){
	next[++tot]=first[x],first[x]=tot,to[tot]=y;
}
/*------------------------dfs---------------------------*/
void dfs1(int u,int f){
	size[u]=1;
	for(int i=first[u];i;i=next[i]){
		int t=to[i]; if(t==f) continue;
		fa[t]=u,dep[t]=dep[u]+1; dfs1(t,u); size[u]+=size[t];
		if(size[t]>size[son[u]]) son[u]=t; 
	}
}
void dfs2(int u,int Top){
	top[u]=Top,id[u]=++sign,pre[sign]=u;
	if(son[u]) dfs2(son[u],Top);
	for(int i=first[u];i;i=next[i]){
		int t=to[i]; 
		if(t==fa[u]||t==son[u]) continue;
		dfs2(t,t);
	}
}
/*------------------------线段树---------------------------*/
void Pushup(int x){
	t[x].val=t[x<<1].val^t[x<<1|1].val;
}
void build(int x,int l,int r){
	t[x].l=l,t[x].r=r;
	if(l==r){t[x].val=a[pre[l]];return;}
	int mid=(l+r)>>1;
	build(x<<1,l,mid),build(x<<1|1,mid+1,r);
	Pushup(x);
}
void update(int x,int pos,int val){
	if(t[x].l==t[x].r){t[x].val=val;return;}
	int mid=(t[x].l+t[x].r)>>1;
	if(pos<=mid) update(x<<1,pos,val);
	else update(x<<1|1,pos,val);
	Pushup(x); 
}
int quary(int x,int L,int R){
	if(L<=t[x].l && t[x].r<=R) return t[x].val;
	int mid=(t[x].l+t[x].r)>>1;
	if(L>mid) return quary(x<<1|1,L,R);
	else if(R<=mid) return quary(x<<1,L,R);
	else return quary(x<<1,L,R)^quary(x<<1|1,L,R);
}
/*------------------------树剖---------------------------*/
void Quary(int x,int y){
	int ans=0;
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		ans^=quary(1,id[top[x]],id[x]);
		x=fa[top[x]]; 
	}
	if(dep[x]<dep[y]) swap(x,y);
	ans^=quary(1,id[y],id[x]);
	if(ans==0) printf("No\n");
	else printf("Yes\n");
} 
int main(){
	n=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<n;i++){
		int x=read(),y=read();
		add(x,y),add(y,x);
	}
	dep[1]=1,dfs1(1,0);
	dfs2(1,1); build(1,1,n);
	q=read();
	while(q--){
		scanf("%s",s);
		int x=read(),y=read();
		if(s[0]=='C') update(1,id[x],y);
		if(s[0]=='Q') Quary(x,y);
	}return 0;
} 

猜你喜欢

转载自blog.csdn.net/sslz_fsy/article/details/83317397