(SG)函数乱胡一气
将公平组合游戏转化为有向图游戏
在一个有向无环图中,只有一个起点,上面有一个棋子,两个玩家轮流沿着有向边推动棋子,不能走的玩家判负。
定义(mex)函数为不属于集合(S)的最小非负整数
[mex(S)=min{x}(x
otin S,xin 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) ot=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");
}
}