题意:
有n个珠子,每次必须涂色m个连续的。这 n个珠子是个环。询问胜负情况
思路:
对于是个环的情况,我们可以首先拿出一组m,如果n<m,先手必输。否则的话跑sg函数,注意此时sg函数跑的是后手的sg情况。
这样环就转变为了链结构,对于一条链,sg[ i<m]=0, sg[m]=1, 每一个sg情况等于子问题的异或。
因此转变为了 对于i>m的情况下, 如果从中拿出m个连续珠子。
子问题是: 假如在j处取这m个珠子, 左侧 j,右侧 i-m-j
#include <stdio.h>
#include <string.h>
#define N 20
const int MAXN=1005;
int f[N],SG[MAXN],S[MAXN];
//f[N]:可改变当前状态的方式,N为方式的种类,f[N]要在getSG之前先预处理
//SG[]:0~n的SG函数值
//S[]:为x后继状态的集合
void getSG(int n,int m)
{
int i,j;
memset(SG,0,sizeof(SG));
//因为SG[0]始终等于0,所以i从1开始
SG[m]=1;
for(i = m+1; i <= n; i++)
{
//每一次都要将上一状态 的 后继集合 重置
memset(S,0,sizeof(S));
for(j = 0; j<i-m; j++)
S[SG[j]^SG[i-m-j]] = 1; //将后继状态的SG函数值进行标记
for(j = 0;; j++)
if(!S[j])
{
//查询当前后继状态SG值中最小的非零值
SG[i] = j;
break;
}
}
}
int main()
{
int n,m,k;
int cs=1;
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n ,&m);
printf("Case #%d: ",cs++);
if(n<m)
{
printf("abcdxyzk\n");
}
else
{
n=n-m;
getSG(n,m);
if(SG[n])
{
printf("abcdxyzk\n");
}
else
printf("aekdycoin\n");
}
}
return 0;
}