xsy1436-括号游戏
题目
递归定义括号序列:
- 空串是括号序列
- (A)是一个括号序列,其中A为括号序列
- AB是一个括号序列,其中A,B均为括号序列
定义严格括号序列为形如(A)的括号序列,其中A为括号序列。
给出一个长度为\(n(n\le 10^3)\)的合法括号序列,两个人进行游戏,每次一个人取走一个严格括号序列,不能再取的算输,问先手必胜还是后手必胜。
分析
好题。严格括号序列可以看成一棵树。一个严格括号序列的每个左括号表示一个点深搜进入,右括号表示遍历完所有的子树了。一个不严格的括号序列可以看成一堆树,就是一个森林。这个游戏完全等价于在一个森林中轮流删除一个点及其子树。这是一个经典问题。
Green Hackenbush Game
砍树游戏是指,在一个有根树组成的森林中,两个玩家轮流砍掉树上的一条边,那条边连出去的子树都被砍掉。无法操作者输。
如何考察这个问题?首先考虑最简单的形式,所有的有根树都是一条链,那么这个问题就完全等价于一个Nim游戏,因为每一次都是在一棵树上的\(n\)个点中删去\(a\in [1,n]\)个点,相当于在许多堆石子中每次选一堆,取出任意多个。从SG函数的角度看,对于一条链,它的SG值其实就是链的长度。
接下来考虑如何把一颗分叉的树转化为链的形式。
Colon Principle
对于树上的一个分叉的节点,所有的分叉都是一条链,那么可以把这些分叉换成一条长度为它们异或和的链,新树与原树在游戏中等价。
有了这个性质,我们只要自下而上把所有的分叉换成链,到最后整棵树形成一条链,我们就得到整棵树的SG值了。
下面我们来证明这个性质。
有两颗随机的树\(H_1,H_2\),满足它们的SG函数值是相同的。现在有一棵树\(G\)和上面一个定点\(x\),用\(G_1\)表示把\(H_1\)接在\(G\)的\(x\)点上形成的树,\(G_2\)表示把\(H_2\)接在\(G\)的\(x\)点上形成的树。我们现在要证明的就是,这两棵树根节点的SG函数值是相同的。这其实就是在证明两棵树的根节点的SG值异或为0。由于多个子游戏拼成的大游戏的SG值就是每个子游戏的SG值异或起来,所以这个证明等价于把这两棵树放在一起做游戏,规则是每次砍掉任意一棵树的任意一条边,后手必胜。一种显然的后手必胜方法就是,对于不涉及到\(H\)内部的操作,后手模仿先手的操作,因为两棵树除了\(H\)的部分是完全一样的。对于涉及到\(H\)内部的操作,即只涉及子树内的操作,我们可以看成是\(H_1\)和\(H_2\)单独做游戏。由于\(H_1\)和\(H_2\)的SG函数值是一样的,所以这两颗树的单独游戏也是后手必胜的。这样整个游戏就是后手必胜的了,故结论成立。
非常巧妙的构造和证明!
代码
#include<cstdio>
#include<cctype>
#include<cstring>
using namespace std;
int read() {
int x=0,f=1;
char c=getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
for (;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
const int maxn=1e3+1;
char s[maxn];
int sta[maxn],top,n,tim=0,ck[maxn];
struct edge {
int v,nxt;
} e[maxn<<1];
int h[maxn],tot=0,ans,f[maxn],all;
void add(int u,int v) {
if (ck[u]!=tim) ck[u]=tim,h[u]=0;
e[++tot]=(edge){v,h[u]};
h[u]=tot;
}
void dfs(int x) {
f[x]=0;
if (ck[x]!=tim) h[x]=0;
for (int i=h[x],v=e[i].v;i;i=e[i].nxt,v=e[i].v) dfs(v),f[x]^=f[v];
++f[x];
}
int main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif
int T=read();
while (T--) {
scanf("%s",s+1);
n=strlen(s+1);
top=ans=0;
for (int i=1;i<=n;++i) {
if (!top) {
++tim,tot=all=0;
sta[++top]=++all;
continue;
}
if (s[i]=='(') {
int u=sta[top];
sta[++top]=++all;
add(u,all);
} else --top;
if (!top) {
dfs(1);
ans^=f[1];
}
}
puts(ans?"peipei":"iamcs");
}
return 0;
}