suxxsfe

一言(ヒトコト)

P3235 [HNOI2014]江南乐(Multi-Nim)

讲博弈论的资料:https://share.weiyun.com/5CSI7PD
https://www.cnblogs.com/zwfymqz/p/8470854.html


multi-nim,就是一个状态的 后继状态 可以为 多个单一游戏,比如还是取石子,但是每次除了取任意颗,还可以把一堆分成两堆不为空的石子
分解为两个游戏的话,可以根据 SG 定理直接把两个游戏的 SG 值异或起来,再和别的一块取 \(mex\)
这种游戏的结论是:

\[SG\left( x\right) =\begin{cases}x-1\left( x\mod4=0\right) \\ x\left( x\mod4=1 \lor 2\right) \\ x+1\left( x\mod4=3\right) \end{cases} \]


https://www.luogu.com.cn/problem/P3235
再看这题,就是分成若干个 \(\lfloor \dfrac{x}{m}\rfloor\)\(\lceil \dfrac{x}{m} \rceil\),个数分别是 \(m-x \bmod m\)\(x\bmod m\)
然后可以直接暴力算 SG 值了
但直接暴力算肯定不行,但考虑到之和个数的奇偶性有关(多的都异或没了),于是 \(x \bmod m\) 最多有两次贡献(奇数和偶数),就可以整除分块,每个 \(\lfloor \dfrac{x}{m}\rfloor\) 的取值只需跑两次
于是就做完了,这题能黑?

#include<cstdio>
#include<algorithm>
#include<cstring>
#define reg register
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=getchar();}
	return y?x:-x;
}
#define N 100006
int S[N],sg[N];
inline int SG(int x){
	for(reg int i=2;i<=x;i=x/(x/i)+1){
		int a=x/i,b=a+1;
		for(reg int j=i,r=x/(x/i);j<=std::min(i+1,r);j++)
			S[(sg[b]*((x%j)&1))^(sg[a]*((j-x%j)&1))]=x;
	}
	for(reg int i=0;;i++)if(S[i]^x) return i;
}
int main(){
	int T=read(),F=read();
	for(reg int i=F;i<=100000;i++) sg[i]=SG(i);
	while(T--){
		int ans=0;
		int n=read();
		while(n--) ans^=sg[read()];
		printf("%d ",ans>0);
	}
	return 0;
} 
posted @ 2021-03-05 14:39  suxxsfe  阅读(94)  评论(0编辑  收藏  举报