【BZOJ1188】分裂游戏(博弈论)

【BZOJ1188】分裂游戏(博弈论)

题面

BZOJ
洛谷

题解

这道题目比较神仙。
首先观察结束状态,即\(P\)状态,此时必定是所有的豆子都在最后一个瓶子中。
发现每次的转移一定是拿出一棵豆子,放两颗豆子,所以一个瓶子中无论豆子数量是多少,我们都可以把所有的豆子拆开看成单个的\(Nim\)游戏(因为迟早都要全部进入到\(n\)号瓶子的)
发现如果有两个在同位置的豆子,胜负结果是不会改变的,因为后手可以一直模仿先手的动作进行单个游戏。因此所有位置的豆子等价于这个位置的豆子总数对于\(2\)的余数。
那么,现在问题变成了,给你一棵豆子,他在\(i\)位置,回答胜负情况。
那么预处理\(SG\)函数即可。这个\(SG\)函数从后往前求。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define MAX 50
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int n,a[MAX],SG[MAX];
bool vis[MAX];
int main()
{
	int T=read();
	while(T--)
	{
		n=read();memset(SG,0,sizeof(SG));
		for(int i=1;i<=n;++i)a[i]=read();
		for(int i=n-1;i;--i)
		{
			memset(vis,0,sizeof(vis));
			for(int j=i+1;j<=n;++j)
				for(int k=j;k<=n;++k)
					vis[SG[j]^SG[k]]=true;
			for(int j=0;;++j)if(!vis[j]){SG[i]=j;break;}
		}
		int cnt=0,A=0,B=0,C=0,sg=0;
		for(int i=1;i<=n;++i)if(a[i]&1)sg^=SG[i];
		for(int i=1;i<=n;++i)
			if(a[i])
				for(int j=i+1;j<=n;++j)
					for(int k=j;k<=n;++k)
						if(!(sg^SG[i]^SG[j]^SG[k]))
						{
							if(!cnt)A=i,B=j,C=k;
							++cnt;
						}
		printf("%d %d %d\n%d\n",A-1,B-1,C-1,cnt);
	}
	return 0;
}

posted @ 2018-08-16 22:12  小蒟蒻yyb  阅读(293)  评论(0编辑  收藏