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

题面

BZOJ
洛谷

题解

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

#include
#include
#include
#include
#include
#include
#include
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;
}

你可能感兴趣的:(BZOJ,各省省选)