题解: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 MBSubmit: 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
No
Yes
Yes
Yes