SG函数胡乱一气
\(SG\)函数乱胡一气
将公平组合游戏转化为有向图游戏
在一个有向无环图中,只有一个起点,上面有一个棋子,两个玩家轮流沿着有向边推动棋子,不能走的玩家判负。
定义\(mex\)函数为不属于集合\(S\)的最小非负整数
\[mex(S)=\min\{x\}(x\not\in S,x\in N)
\]
\(eg:mex(\{1,2\})=0\)
对于状态\(x\),它有\(k\)个后继状态\(y_1,y_2...y_k\),定义\(SG\)函数
\[SG(x)=mex\{SG(y_1),SG(y_2),...SG(y_k) \}
\]
SG定理:对于有向图组合游戏,起点为\(s_1,s_2...s_n\),有定理,当且仅当\(SG(s_1)\oplus SG(s_2)\oplus...\oplus SG(s_n)\not=0\)时,游戏先手必胜
\(SG\)问题初始化时必须指明必败状态
例题\(1:HDU1848~~Fibonacci~again~and~again\)
共\(3\)堆石子,每人任选一堆取走\(f\)个,\(f\)必须为斐波那契数,最后取完石子的人胜,先后手谁赢?
定义\(SG[x]\)表示石子个数为\(x\)的\(SG\)值,\(SG[0]=0\),为必败态
int f[20],n,m,p,sg[N],vis[N];
int solve(int x){
if(~sg[x]) return sg[x];
memset(vis,0,sizeof(vis));
for(int i = 1;i <= 15;++i){
if(x < f[i]) break;
vis[solve(x - f[i])] = 1;//转移状态
}
for(int i = 0;i <= 2000;++i)
if(!vis[i]) sg[x] = i;//mex过程
}
int main(){
f[0] = f[1] = 1; f[2] = 2;
for(int i = 3;i <= 15;++i) f[i] = f[i-1] + f[i-2];
memset(sg,-1,sizeof(sg)); sg[0] = 0;
while(~scanf("%d%d%d",&n,&m,&p)){
if(n == m == p == 0) return 0;
if(solve(n) ^ solve(m) ^ solve(p) == 0) puts("Nacci"); //后手胜
else puts("Fibo"); //先手胜
}
}
\(P2148~~[SDOI2009]E\&D\)
有一些游戏,每个游戏都是这样的:
有两堆石子,每次操作可以丢掉其中一堆,并且把另一堆分至少一个到丢掉的这堆,保证每堆至少有一个石子,不能操作的人输。
每次可以挑一个游戏操作,最后不能操作的人输。
根据\(SG\)定理,求出每一组的\(SG\)值再全部异或起来
状态\((a,b)\)可以转移到\((c,d)\{c+d=a\)或\(c+d=b\}\)
打表吧
#define bitset<N> B
B s[M]; int ans[N][N];
inline int mex(B b){
int i = 0; while(b[i]) ++i; return i;}
int main(){
int i,j,k;
for(i=2;i<=N;++i)
for(j=1,k=i-1;k;++j,--k)
s[i].set(ans[j][k]=mex(s[j]|s[k]));//枚举合并
for(i=0;i<N;++i)printf("%3d",i);puts("");
for(i=1;i<N;++i){//输出矩阵
printf("%2d:",i);
for(j=1;i+j<=N;++j)
printf("%3d",ans[i][j]); puts("");
}
for(i=1;i<=N;++i){//输出对于每一个a,所有c+d=a的(c,d)的SG值集合
printf("%2d:SG%d ",i,mex(s[i]));
cout<<s[i]<<endl;
}
}
表
0: 1 2 3 4 5 6 7 8 9
1: 0 1 0 2 0 1 0 3 0
2: 1 1 2 2 1 1 3 3
3: 0 2 0 2 0 3 0
4: 2 2 2 2 3 3
5: 0 1 0 3 0
6: 1 1 3 3
7: 0 3 0
8: 3 3
9: 0
然后
int main(){
int T = read(),ans,n,x,cnt;
while(T--){
ans = 0;
n = read() >> 1;
while(n--){
cnt = 0;
x = (read() - 1) | (read() - 1);
while(x & 1) ++cnt,x >>= 1;
ans ^= cnt;
}
puts(ans ? "YES" : "NO");
}
}