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;
}
posted @ 2017-04-17 20:20  permui  阅读(411)  评论(0编辑  收藏  举报