版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/C20181220_xiang_m_y/article/details/88595579
题目传送门
题目分析:
每堆石子是独立的,可以求出每堆的SG函数再异或起来。
所以考虑预处理(或者记忆化搜索)SG函数。
设石子数量为
,枚举它分成的堆数
,那么较小堆的石子数是
(较大堆是
+1),设
的余数是
,那么大堆的数量是
,小堆的数量是
,根据
和
的奇偶性进行异或得到分成
堆的SG值,在vis数组中标记一下就行了。
但是这样做是 的,注意到 只有 个取值(画一个反比例函数根据对称性就看得出来),考虑整除分块优化。
若
,大小堆的石子数是一样的,那么多出来的一堆肯定是从
个大堆里面选
堆,每堆取出一个石子放到一起形成一个新堆,对应的
,由于是根据奇偶性进行异或并标记vis数组,那么
相等的一段最多只会有i和i+1两种不同的贡献,i+2的大小堆堆数的奇偶性与i相同,所以一段只需要算两次就可以了。
复杂度
Code:
#include<cstdio>
#include<cctype>
#define maxn 100005
inline void read(int &a){
char c;while(!isdigit(c=getchar()));
for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
}
int SG[maxn],F,T,n,vis[maxn];
int calc(int k){
int x,y,s;
for(int i=2,j;i<=k;i=j+1){
s=k/i;
y=k%i,x=i-y;
vis[((x&1)*SG[s])^((y&1)*SG[s+1])]=k;
j=k/s;
if(i<j) y=k%(i+1),x=i+1-y,vis[((x&1)*SG[s])^((y&1)*SG[s+1])]=k;
}
x=0;while(vis[x]==k) x++;
return x;
}
void Pre(){for(int i=F;i<=100000;i++) SG[i]=calc(i);}
int main()
{
read(T),read(F);
Pre();
while(T--){
read(n);int x=0,y;
while(n--) read(y),x^=SG[y];
printf("%d%c",x?1:0,T?32:10);
}
}