所有后继状态的\(SG\)值异或起来等于当前状态的\(SG\)值
把\(x\)个石子分成\(i\)堆
我们可以发现最后有\(x % i\)堆分到了\(\lfloor\frac{x}{i}\rfloor+1\)个石子,有\(i-x%i\)堆分到了\(\lfloor\frac{x}{i}\rfloor\)个石子 , 只需要考虑堆数的奇偶即可
又发现分成\(i\)堆和分成\(i+2\)堆是等价的,所以对于同一个块只要烤绿最前面的两个值即可
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define Debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
typedef long long LL;
const int INF=1e9+7;
inline LL read(){
register LL x=0,f=1;register char c=getchar();
while(c<48||c>57){if(c=='-')f=-1;c=getchar();}
while(c>=48&&c<=57)x=(x<<3)+(x<<1)+(c&15),c=getchar();
return f*x;
}
const int N=1e5+5;
int sg[N],mex[N];
int T,F;
inline int getsg(int x){
if(~sg[x]) return sg[x];
if(x<F) return sg[x]=0;
sg[x]=0;
for(int i=2;i<=x;i=x/(x/i)+1){//分成i堆,整除分块
for(int j=i;j<=min(i+1,x);j++){//只要考虑前两个数
int tmp=0;
if((x%j)&1) tmp^=getsg(x/j+1);
if((j-x%j)&1) tmp^=getsg(x/j);
mex[tmp]=x;
}
}
while(mex[sg[x]]==x) sg[x]++;//后继的补集中最大的数
return sg[x];
}
int main(){
T=read(),F=read();
memset(sg,-1,sizeof sg);
while(T--){
int ans=0;
for(int i=read();i;i--) ans^=getsg(read());
printf("%d ",(bool)ans);
}
}
原文:https://www.cnblogs.com/lizehon/p/10613431.html