传送门
思路:
很厉害的一道博弈
虽说是暴力求SG函数,但是要加入分块优化
如果说直接按照规则求的话是
O(n2)
的
(这也是我会的……)
但是注意到
⌊ni⌋
这个神奇的东西
可以联想到分块处理,因为这玩意的取值只有
2n−−√
种
然后我的水平也就仅到于此了……
求了几次都不对……
实际上
把n个石子分成i堆 当n / i == n / (i + 2) (n / i 为分出来以后每堆的最小石子数)时sg值异或和结果相同
这就很好了
也就是说只要计算一次奇数一次偶数就可以解决问题了
注意要求n/i的取值相同……所以算n/(i+1)时判断一下
同时最好边求边做,别一口气全求出来,不然会T……
代码:
#include<cstdio>
#include<cstring>
using namespace std;
int T,F,n;
int a[105],sg[100005],c[100005];
int in()
{
int t=0;char ch=getchar();
while (ch>'9'||ch<'0') ch=getchar();
while (ch>='0'&&ch<='9') t=(t<<1)+(t<<3)+ch-48,ch=getchar();
return t;
}
int SG(int x)
{
if (x<F) return sg[x]=0;
if (sg[x]!=-1) return sg[x];
for (int a,b,t,i=2;i<=x;i=x/(x/i)+1)
{
a=x%i,b=i-a,t=0;
if (b&1) t^=SG(x/i);
if (a&1) t^=SG(x/i+1);
c[t]=x;
if (x/(x/i)>i)
{
++i,
a=x%i,b=i-a,t=0;
if (b&1) t^=SG(x/i);
if (a&1) t^=SG(x/i+1);
c[t]=x;
}
}
for (int i=0;;++i)
if (c[i]!=x) return sg[x]=i;
}
main()
{
memset(sg,-1,sizeof(sg));
T=in();F=in();
for (;T;--T)
{
n=in();
int ans=0;
for (int i=1;i<=n;++i)
a[i]=in(),
ans^=SG(a[i]);
if (ans) putchar('1');
else putchar('0');
if (T>1)putchar(' ');
}
}