【BZOJ4035】数组游戏(博弈论)

【BZOJ4035】数组游戏(博弈论)

题面

BZOJ
洛谷

题解

很明显是一个翻硬币游戏的变形,因此当前局面的(SG)函数值就是所有白格子单独存在的(SG)函数的异或和。
那么,对于每一个位置考虑(SG)函数。
(SG(x)=mex_{i=1}^{n/x}{oplus_{j=1}^i SG(jx) })
这种东西很不好算,直接打个表,
发现对于所有(n/x)相同的数,他们的(SG)函数都是相同的。
那么数论分块一下就只有(O(sqrt n))个有效的的(SG)值了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define MAX 100100
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int SG1[MAX],SG2[MAX],n,Sqr,vis[MAX],tim;
int getSG(int x){return x<=Sqr?SG1[x]:SG2[n/x];}
int nt(int i){if(i==n)return n+1;return n/(n/(i+1));}
void init()
{
	Sqr=sqrt(n);SG1[1]=1;
	for(int i=2,nw;i<=n;i=nt(i))
	{
		++tim;nw=0;vis[nw]=tim;
		for(int j=2,k;j<=i;j=k+1)
		{
			k=i/(i/j);
			vis[nw]=vis[nw^getSG(i/j)]=tim;
			if((k-j+1)&1)nw^=getSG(i/j);
		}
		for(int j=0;;++j)
			if(vis[j]!=tim)
			{
				i<=Sqr?SG1[i]=j:SG2[n/i]=j;
				break;
			}
	}
}
int main()
{
	n=read();init();
	int T=read();
	while(T--)
	{
		int m=read(),s=0;
		while(m--)s^=getSG(n/read());
		puts(s?"Yes":"No");
	}
	return 0;
}

原文地址:https://www.cnblogs.com/cjyyb/p/9503558.html