SG函数用来求博弈过程中某一情况下是先手必败(P-position)还是先手必胜(N-position)
性质:
所有的先手必败(P-position)当且仅当 SG = 0
所有的先手必胜(N-position)当且仅当 SG != 0
结束局面(T-position)SG = 0 ,可以理解为无法继续操作了所以先手必败
SG(x) = mex{ SG(y) | y是x的后继 } (mex是最小的不属于该集合的非负整数,如mex{0,1,3,4} = 2;
*若游戏G是由g1,g2,g3..gk游戏组合而成,相当于下一步将gi游戏的状态变成pi,即下一步的游戏由g1,g2...gi-1,pi,gi+1,...gk-1,gk,此时SG(G) = SG(g1) ^ SG(g2) ^ SG(g3) ... SG(gk)
可以去耐心地看一下百度百科“nim游戏”词条和“SG函数”词条,描述的很清楚
BZOJ1188:
一篇写的差不多的博客:http://www.cnblogs.com/JoeFan/p/4259055.html
先求出来每一a[i] ~ a[n]状态下的SG
然后枚举i,j,k,相当于减去a[i]状态下的游戏,加上a[j],a[k]状态下的游戏
答案的SG就异或上a[i],a[j],a[k]
(感觉自己讲的不是很清楚)
#include <iostream>
#include <cstdio>
#include <cstring>
#define N 50
using namespace std;
int a[N],sg[N],F[N*N],n;
int main()
{
int T = 0; scanf("%d",&T);
while (T--) {
memset(sg,0,sizeof(sg));
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
//预处理单个游戏
for (int i=n;i>=1;i--) {
memset(F,0,sizeof(F));
for (int j=i+1;j<=n;j++)
for (int k=j;k<=n;k++)
F[sg[j] ^ sg[k]] = true;
//SG[x] = mex{ SG[y] | y是x的后继 }
for (int j=0;j<=n*n;j++)
if (!F[j]) {
sg[i] = j; break;
}
}
//for (int i=1;i<=n;i++) printf("%d\n",sg[i]);
int tmp = 0;
for (int i=1;i<=n;i++) if (a[i] & 1) tmp ^= sg[i];
int tot = 0;
for (int i=1;i<=n;i++) if (a[i] != 0) {
for (int j=i+1;j<=n;j++)
for (int k=j;k<=n;k++)
if ((tmp ^ sg[i] ^ sg[j] ^ sg[k]) == 0) {
if (++tot == 1) printf("%d %d %d\n",i-1,j-1,k-1);
}
}
if (!tot) printf("-1 -1 -1\n");
printf("%d\n",tot);
}
return 0;
}