题目
首先这个题SG函数很明显。
枚举分为多少堆然后对子状态的SG函数求mex就行。
但是直接枚举是
O
(
n
2
)
O(n^2)
O(n2)
可以用整除分块:
n个石子分成m堆:
有
n
n
n%
m
m
m个大小为
⌊
n
m
⌋
+
1
\lfloor\frac nm\rfloor+1
⌊mn⌋+1的石堆。
m
−
n
m-n
m−n%
m
m
m个大小为
⌊
n
m
⌋
\lfloor\frac nm\rfloor
⌊mn⌋的石堆。
现在只有
r
=
n
r=n
r=n%
m
m
m和
m
−
r
m-r
m−r的大小不确定了。
发现如果
u
=
⌊
n
m
⌋
u=\lfloor\frac nm\rfloor
u=⌊mn⌋。
m
+
+
m++
m++时
r
−
=
u
r-=u
r−=u,
m
−
r
−
>
m
−
r
+
u
+
1
m-r->m-r+u+1
m−r−>m−r+u+1
所以u为奇数时m-r奇偶不变,r奇偶变。
u为偶数时反之。
AC Code:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100005
using namespace std;
int T,F,n;
int SG[maxn];
int vis[maxn],tim;
int ser(int x){
if(SG[x]>=0) return SG[x];
for(int i=2,nxt;i<=x;i=nxt+1){
nxt=x/(x/i);
int u = x / i;
ser(u),ser(min(u+1,x-1));
}
++tim;
for(int i=2,nxt;i<=x;i=nxt+1){
nxt=x/(x/i);
int u = x / i , r = x % i , SG1 = ser(u) , SG2 = ser(min(u+1,x-1));
vis[(((i-r)&1)*SG1)^((r&1)*SG2)] = tim;
if(i<nxt && r>=u) r-=u,i++,vis[(((i-r)&1)*SG1)^((r&1)*SG2)] = tim;
}
for(SG[x]=0;vis[SG[x]]==tim;SG[x]++);
return SG[x];
}
int main(){
memset(SG,-1,sizeof SG);
scanf("%d%d",&T,&F);
for(int i=0;i<F;i++) SG[i]=0;
SG[1]=0;
for(;T--;){
scanf("%d",&n);
int ans = 0;
for(int i=1,x;i<=n;i++){
scanf("%d",&x);
ans ^= ser(x);
}
if(ans == 0) printf("%d%c",0,T==0?'\n':' ');
else printf("%d%c",1,T==0?'\n':' ');
}
}