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;
}