[HNOI2007]分裂游戏
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 1394 Solved: 847
[Submit][Status][Discuss]
Description
聪聪和睿睿最近迷上了一款叫做分裂的游戏。该游戏的规则试:共有n个瓶子,标号为0,1,2.....n-1,第i个瓶子中
装有p[i]颗巧克力豆,两个人轮流取豆子,每一轮每人选择3个瓶子。标号为i,j,k,并要保证i<j,j<=k且第i个瓶子
中至少要有1颗巧克力豆,随后这个人从第i个瓶子中拿走一颗豆子并在j,k中各放入一粒豆子(j可能等于k)。如
果轮到某人而他无法按规则取豆子,那么他将输掉比赛。胜利者可以拿走所有的巧克力豆!两人最后决定由聪聪先
取豆子,为了能够得到最终的巧克力豆,聪聪自然希望赢得比赛。他思考了一下,发现在有的情况下,先拿的人一
定有办法取胜,但是他不知道对于其他情况是否有必胜策略,更不知道第一步该如何取。他决定偷偷请教聪明的你
,希望你能告诉他,在给定每个瓶子中的最初豆子数后是否能让自己得到所有巧克力豆,他还希望你告诉他第一步
该如何取,并且为了必胜,第一步有多少种取法?
假定 1 < n < = 21,p[i] < = 10000
Input
输入文件第一行是一个整数t表示测试数据的组数,
接下来为t组测试数据(t<=10)。
每组测试数据的第一行是瓶子的个数n,
接下来的一行有n个由空格隔开的非负整数,表示每个瓶子中的豆子数。
Output
对于每组测试数据,输出包括两行,
第一行为用一个空格两两隔开的三个整数,表示要想赢得游戏,
第一步应该选取的3个瓶子的编号i,j,k,
如果有多组符合要求的解,那么输出字典序最小的一组。
如果无论如何都无法赢得游戏,那么输出用一个空格两两隔开的三个-1。
第二行表示要想确保赢得比赛,第一步有多少种不同的取法。
Sample Input
2
4
1 0 1 5000
3
0 0 1
4
1 0 1 5000
3
0 0 1
Sample Output
0 2 3
1
-1 -1 -1
0
1
-1 -1 -1
0
HINT
Source
题解:这道题目,将每颗石子单独作为一个游戏去玩,这样枚举下一次的j,k
最后再xor起来即可。
方法的话就是判断取了这一个后下一个状态是不是必败了即可。
#pragma GCC optimize(2)
#pragma G++ optimize(2)
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring> #define N 27
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-;ch=getchar();}
while(isdigit(ch)){x=(x<<)+(x<<)+ch-'';ch=getchar();}
return x*f;
} int a[N],sg[N];
void pre()
{
bool mark[];
sg[]=;
for(int i=;i<=;i++)
{
memset(mark,,sizeof(mark));
for(int j=;j<i;j++)
for(int k=;k<=j;k++)
mark[sg[j]^sg[k]]=;
for(int j=;;j++)
if(!mark[j]){sg[i]=j;break;}
}
}
int main()
{
int t,n;
pre();
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=;i<=n;i++)scanf("%d",&a[i]);
int ans=,tot=;
for(int i=;i<=n;i++)
if(a[i]&)ans^=sg[n-i+];
for(int i=;i<=n;i++)
for(int j=i+;j<=n;j++)
for(int k=j;k<=n;k++)
if((ans^sg[n-i+]^sg[n-j+]^sg[n-k+])==)
{
tot++;
if(tot==)printf("%d %d %d\n",i-,j-,k-);
}
if(!tot)printf("-1 -1 -1\n");
printf("%d\n",tot);
}
}