BZOJ3729 Gty的游戏
转一发blog
首先看这道题的博弈部分,也就是如何得到答案。对于单个节点来说,这就是一个简单的巴什博弈问题,每次可以拿走[1..L]个的话,只需要把石子个数对L+1取模就可以得到它的SG值了。如果考虑树上操作这就是一个阶梯博弈,每次只需要考虑奇数层上的石子。那么只需要对每棵子树维护它的奇数层SG值和偶数层SG值就可以了。因为有插入节点的操作所以dfs序要用Splay来维护。
这道题里面最关键的操作是插入节点的操作。因为访问子树的时候要知道它的入栈点和出栈点,也就是in和out,而如果插入了一个节点就有可能引起很多节点out值的变化。
一个节点的in显然就是它自己,但out值是它子树里最后一个被遍历的节点,也就是它一定是一个叶子节点。那么如果在某个叶子节点下面接了一个节点,就有至少一个节点的out会变化。而我们可以发现这个寻找out的关系是具有传递性的,比如树上某个节点x把u当做它的out,而u当前的out节点指向新接入的儿子v,那么x的out也应该指向v。这就启示我们可以用并查集来维护这个东西,把每个节点并到它的out上去就可以了。
而为了让修改out的次数尽量少,如果接入新节点的那个父亲不是叶子节点,我们可以通过适当处理让所有节点的out都不变,方法就是把新节点的dfs序紧挨着父节点放在它后面。比如当前有dfs序:u-v-w,v和w都是u的儿子,那么显然u的out是w。如果现在还要给u接一个新儿子x,那么我们可以把dfs序修改成:u-x-v-w,这样的话所有已有节点的in和out都不会变。而如果修改成:u-v-w-x,u的out就变了,就造成了不必要的修改。
http://blog.csdn.net/fromatp/article/details/54800000
我们先来看一道POJ1704阶梯博弈题,做法就是将偶的并在一起,奇的话就将1和0并在一起,变成nim博弈。
#include<cstdio> #include<cstdlib> #include<iostream> #include<cstring> #include<algorithm> using namespace std; int a[1005],n,T; int main() { scanf("%d",&T); while(T--) { scanf("%d",&n);int sum=0; for(int i=1;i<=n;++i)scanf("%d",&a[i]); sort(a+1,a+1+n); for(int i=(n&1)^1;i<=n;i+=2) sum=sum^(a[i+1]-a[i]-1); if(sum==0)puts("Bob will win"); else puts("Georgia will win"); } }
然后是本题代码
#include<bits/stdc++.h> using namespace std; const int N=2e5+10; int val[N],fa[N],c[N][2],sg[N][2],typ[N],a[N],dep[N],pos[N][2],st[N]; int n,mod,rt,num,tot,m,head[N],ccnt; map<int,int>id; struct node { int to,nex; }e[N]; void add(int x,int y) { e[++ccnt].to=y;e[ccnt].nex=head[x];head[x]=ccnt; } void update(int x) { sg[x][0]=sg[c[x][0]][0]^sg[c[x][1]][0]; sg[x][1]=sg[c[x][0]][1]^sg[c[x][1]][1]; sg[x][typ[x]]=sg[x][typ[x]]^val[x]; } void rotate(int x,int &k) { int y=fa[x],z=fa[y],l,r; if(c[y][0]==x)l=0;else l=1;r=l^1; if(y==k)k=x; else {if(c[z][0]==y)c[z][0]=x;else c[z][1]=x;} fa[x]=z;fa[y]=x;fa[c[x][r]]=y; c[y][l]=c[x][r];c[x][r]=y; update(y);update(x); } void splay(int x,int &k) { while(x!=k) { int y=fa[x],z=fa[y]; if(y!=k) { if(c[y][0]==x^c[z][0]==y)rotate(x,k); else rotate(y,k); } rotate(x,k); } } void query(int x) { splay(pos[x][0],rt); splay(pos[x][1],c[rt][1]); if(sg[c[pos[x][1]][0]][dep[x]^1]) { num++;puts("MeiZ"); } else puts("GTY"); } void build(int l,int r,int f) { if(l>r)return; int mid=l+r>>1; c[f][mid>=f]=mid;fa[mid]=f; pos[abs(st[mid])][st[mid]<0]=mid; if(st[mid]>0)val[mid]=a[st[mid]]; typ[mid]=dep[abs(st[mid])]; build(l,mid-1,mid);build(mid+1,r,mid); update(mid); } void dfs(int x) { st[++tot]=x; for(int i=head[x];i;i=e[i].nex) { int y=e[i].to; dep[y]=dep[x]^1; dfs(y); } st[++tot]=-x; } void insert(int x,int y,int w) { splay(pos[x][0],rt); int k=c[rt][1];while(c[k][0])k=c[k][0]; int t1=++tot,t2=++tot; pos[y][0]=t1;pos[y][1]=t2;fa[t1]=k;fa[t2]=t1; dep[y]=dep[x]^1;c[k][0]=t1;c[t1][1]=t2;val[t1]=w; typ[t1]=typ[t2]=dep[y]; update(t2);update(t1);update(k);splay(t2,rt); } int main() { scanf("%d%d",&n,&mod);mod++; for(int i=1;i<=n;++i) { scanf("%d",&a[i]);a[i]%=mod; id[i]=i; } int x,y,f,z; for(int i=1;i<n;++i) { scanf("%d%d",&x,&y); add(x,y); } dfs(1);build(1,tot,0);rt=1+tot>>1; scanf("%d",&m); for(int i=1;i<=m;++i) { scanf("%d",&f); if(f==1){ scanf("%d",&x);x=x^num; query(id[x]); } else if(f==2){ scanf("%d%d",&x,&y);x^=num;y^=num;y%=mod; splay(pos[id[x]][0],rt);val[rt]=y;update(rt); } else{ scanf("%d%d%d",&x,&y,&z); x^=num;y^=num;z^=num;z%=mod;id[y]=++n; insert(id[x],n,z); } } return 0; }