首页 > 代码库 > 【BZOJ 2819】 Nim
【BZOJ 2819】 Nim
2819: Nim
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 926 Solved: 355
[Submit][Status]
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
HINT
Source
湖北省队互测
裸的树链剖分。
没有用dfs也没有爆栈啊。。
手工栈怎么写???
用线段树维护异或值就可以了。
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #define M 500005 using namespace std; int id[M],n,num,a[M],tot,no[M],h[M],size[M],dep[M],top[M],fa[M],son[M]; struct edge { int y,ne; }e[M*2]; struct segtree { int l,r,v; }t[M*3]; void read(int &tmp) { tmp=0; int fu=1; char ch=getchar(); for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') fu=-1; for (;ch>='0'&&ch<='9';ch=getchar()) tmp=tmp*10+ch-'0'; tmp*=fu; } void Addedge(int x,int y) { e[++tot].y=y; e[tot].ne=h[x]; h[x]=tot; } void dfs1(int x,int f,int de) { dep[x]=de; size[x]=1; son[x]=0; fa[x]=f; for (int i=h[x];i;i=e[i].ne) if (e[i].y!=f) { int y=e[i].y; dfs1(y,x,de+1); size[x]+=size[y]; if (size[son[x]]<size[y]) son[x]=y; } } void dfs2(int x,int tp) { top[x]=tp; id[x]=++num; no[num]=x; if (son[x]) dfs2(son[x],tp); for (int i=h[x];i;i=e[i].ne) { int y=e[i].y; if (y==fa[x]||y==son[x]) continue; dfs2(y,y); } } void Push_up(int x) { t[x].v=t[x<<1].v^t[(x<<1)+1].v; } void Build(int x,int l,int r) { t[x].l=l,t[x].r=r; if (l==r) { t[x].v=a[no[l]]; return; } int m=(l+r)>>1; Build(x<<1,l,m); Build((x<<1)+1,m+1,r); Push_up(x); } int Getxor(int x,int l,int r) { if (t[x].l>=l&&t[x].r<=r) return t[x].v; int m=(t[x].l+t[x].r)>>1; if (r<=m) return Getxor(x<<1,l,r); if (l>m) return Getxor((x<<1)+1,l,r); return Getxor(x<<1,l,r)^Getxor((x<<1)+1,l,r); } int Query(int x,int y) { int tp1=top[x],tp2=top[y]; int ans=0; while (tp1!=tp2) { if (dep[tp1]<dep[tp2]) swap(tp1,tp2),swap(x,y); ans^=Getxor(1,id[tp1],id[x]); x=fa[tp1]; tp1=top[x]; } if (x==y) return ans^a[x]; if (dep[x]>dep[y]) swap(x,y); ans^=Getxor(1,id[x],id[y]); return ans; } void Modify(int x,int p) { if (t[x].l==t[x].r) { t[x].v=a[no[p]]; return; } int m=(t[x].l+t[x].r)>>1; if (p<=m) Modify(x<<1,p); else Modify((x<<1)+1,p); Push_up(x); } int main() { read(n); for (int i=1;i<=n;i++) read(a[i]); for (int i=1;i<n;i++) { int x,y; read(x),read(y); Addedge(x,y),Addedge(y,x); } dfs1(1,0,1); dfs2(1,1); Build(1,1,num); int q; read(q); for (int i=1;i<=q;i++) { char str[3]; scanf("%s",str); int x,y; read(x),read(y); if (str[0]=='Q') { if (Query(x,y)) printf("Yes\n"); else printf("No\n"); } else { a[x]=y; Modify(1,id[x]); } } return 0; }
感悟:
1A了~
注意写Query时,if (x==y) 还要异或当前结点的值再return
【BZOJ 2819】 Nim