题意:
有n堆石头,编号为1-n;
每次操作为选择三个堆,i<j<=k;
取走i中的一个石子并在j,k两堆都放入一个;
不能操作的人输,求先手能否必胜;
若能则输出第一步的字典序最小方案和方案数;
题解:
博弈论的题目一般就是组合游戏加SG函数;
但是稍微一考虑却发现各个石头堆不是独立的,不能直接上组合游戏;
所以这题是HNOI的题,考虑每个石头作为一个游戏;
会发现这样就是独立的游戏了!
那么定义SG函数f[x]表示离第n堆还有x堆的一颗石头的SG值;
显然f[0]=0,枚举比x小的i,j就是x的后继状态,打出了SG函数表;
然后对每个堆中的石头求异或和,求和不为0则先手必胜;
输出方案啥的暴力枚举O(n^3)可以解决;
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 50
using namespace std;
int f[N],a[N];
bool hash[N];
int sg(int x)
{
memset(hash,0,sizeof(hash));
for(int i=0;i<x;i++)
for(int j=0;j<=i;j++)
hash[f[i]^f[j]]=1;
for(int i=0;;i++)
{
if(!hash[i])
{
f[x]=i;
break;
}
}
}
void init()
{
for(int i=1;i<=21;i++)
sg(i);
}
int main()
{
int c,T,n,m,i,j,k,l,ans,cnt;
bool flag;
init();
scanf("%d",&T);
for(c=1;c<=T;c++)
{
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",a+i);
for(i=1,ans=0;i<=n;i++)
if(a[i]&1)
ans^=sg(n-i);
if(ans==0)
{
printf("-1 -1 -1\n0\n");
continue;
}
for(i=1,flag=0,cnt=0;i<=n;i++)
{
for(j=i+1;j<=n;j++)
{
for(k=j;k<=n;k++)
{
if((ans^f[n-i]^f[n-j]^f[n-k])==0)
{
if(!flag)
printf("%d %d %d\n",i-1,j-1,k-1),flag=1;
cnt++;
}
}
}
}
printf("%d\n",cnt);
}
return 0;
}