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");
	}
}
posted @ 2020-10-19 21:10  INFP  阅读(103)  评论(0编辑  收藏  举报