题意:
两个人轮流给有N个玻璃球的环涂色,每次只能给一段长为M的子段且没有涂过色的玻璃球涂色,不能操作者输
分析:
SG函数:
SG[x] = mex{SG[y] | y为x能到达的后继点},mex{}返回在自然数中第一个未出现的数,mex{0,1,3,4} = 2;SG[x] = 0,则x为必败点,否则x为必胜点
SG定理:
子游戏SG值的异或和等于全局的SG值
NIM博弈:
给出N堆石子,两人轮流选择一堆拿出其中的任意值,不能拿则输
先考虑一堆N个石子,由于可以拿任意的数量,那么它能到达的后继点为[0,N-1],SG[1] = 1,SG[2] = 2,SG[N] = N,每一堆石子就是子游戏,由SG定理,每堆石子个数的异或和就是全局的SG值,判断全局的SG值便知输赢
再看此题:
先手拿了M个后,环就变成了长为N-M的链,那么它的后继点为这条链的某一段,而拿了某一段后就剩下了左右两段,相当于两个子游戏,假设后继点为拿了[L,R]这段,那么由SG定理,这个后继点的SG值为:SG[L-1]^SG[N-R],也就是左右两边剩余长度的SG值的异或和
注意:我们是在先手首次拿了后计算的SG函数,那么后手就变成了先手
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3+13;
int SG[maxn],n,m,vis[maxn],T;
void initSG(int n,int m)
{
for(int i = 1; i <= n; ++i)
{
memset(vis,0,sizeof(vis));
for(int j = 0; j <= i-m; ++j)
{
vis[SG[j]^SG[i-j-m]] = 1;
}
for(int j = 0; j <= i; ++j)
{
if(!vis[j])
{
SG[i] = j;
break;
}
}
}
}
int main()
{
cin >> T;
for(int i = 1; i <= T; ++i)
{
cin >> n >> m;
printf("Case #%d: ",i);
if(m > n)
{
puts("abcdxyzk");
continue;
}
initSG(n-m,m);
if(SG[n-m]) puts("abcdxyzk");
else puts("aekdycoin");
}
return 0;
}