BZOJ2819: Nim

题解:nim博弈 把问题转化成 求一条链上异或和是否为0 这样的话最直观的写法就是熟练剖分+BIT了  但是考虑到异或的性质 我们可以构造差分维护子树 用lca解决问题 复杂度只有一个log 但是我比qko的2个log还慢TAT

#include <bits/stdc++.h>
const int MAXN=5e5+10;
using namespace std;
int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f*x;
}
int n,q;
int a[MAXN];
vector<int>vec[MAXN];
int dep[MAXN],fa[MAXN],num[MAXN],son[MAXN];
int p[MAXN];
int cnt;
void dfs1(int v,int pre,int deep){
    p[v]=++cnt;fa[v]=pre;num[v]=1;dep[v]=deep+1;
    for(int i=0;i<vec[v].size();i++){
	if(vec[v][i]!=pre){
	    dfs1(vec[v][i],v,deep+1);
	    num[v]+=num[vec[v][i]];
	    if(son[v]==-1||num[son[v]]<num[vec[v][i]])son[v]=vec[v][i];
	}
    }
}
int tp[MAXN];
void dfs2(int v,int td){
    tp[v]=td;
    if(son[v]!=-1)dfs2(son[v],td);
    for(int i=0;i<vec[v].size();i++){
	if(son[v]!=vec[v][i]&&fa[v]!=vec[v][i])dfs2(vec[v][i],vec[v][i]);
    }
}
int d[MAXN];
int get_id(int x){return x&(-x);}
void add(int x,int vul){
    for(int i=x;i<=n;i+=get_id(i))d[i]^=vul;
}
int Lca(int u,int v){
    int uu=tp[u];int vv=tp[v];
    while(uu!=vv){
	if(dep[uu]<dep[vv])swap(uu,vv),swap(u,v);
	u=fa[uu];uu=tp[u];
    }
    if(dep[u]>dep[v])swap(u,v);
    return u;
}
int Sum(int x){
    int ans=0;
    for(int i=x;i>0;i-=get_id(i))ans^=d[i];
    return ans;
}
int main(){
    n=read();
    for(int i=1;i<=n;i++)a[i]=read(),son[i]=-1;
    int u,v;
    for(int i=1;i<n;i++)u=read(),v=read(),vec[u].push_back(v),vec[v].push_back(u);
    dfs1(1,0,0);
  //  cout<<"sb"<<endl;
    dfs2(1,1);
   // cout<<"sb"<<endl;
   // for(int i=1;i<=n;i++)cout<<p[i]<<" ";
   // cout<<endl;
    for(int i=1;i<=n;i++)add(p[i],a[i]),add(p[i]+num[i],a[i]);
   // cout<<"sb"<<endl;
    int q;q=read();char ch;
    for(int i=1;i<=q;i++){
	scanf(" %c",&ch);
	if(ch=='Q'){
	    u=read(),v=read();int lca=Lca(u,v);
//	    cout<<lca<<endl;
//	    cout<<p[u]<<" "<<p[v]<<endl;
	    int t=Sum(p[u])^Sum(p[v]);
//	    cout<<t<<endl;
	    t^=a[lca];
//	    cout<<t<<" "<<a[lca]<<endl;
	    if(t)puts("Yes");
	    else puts("No");
	}
	else{
	    u=read();v=read();
	    add(p[u],a[u]);add(p[u]+num[u],a[u]);
	    add(p[u],v);add(p[u]+num[u],v);
	    a[u]=v;
	}
    }
}

 

2819: Nim

Time Limit: 20 Sec  Memory Limit: 128 MB
Submit: 3605  Solved: 1354
[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

Yes
No
Yes
Yes
Yes

 

posted @ 2018-06-23 00:27  wang9897  阅读(120)  评论(0编辑  收藏  举报