[SDOI 2016] 硬币游戏

时间:2021-03-03 16:52:41

题目描述:

雾.

题目分析:

首先我们可以发现,每个数的形式都是这样的:c∗2a∗3b
如果我们建立一个二维的坐标系,横坐标表示a,纵坐标表示b,可以这样表示的原因是因为这个游戏的规则。每次翻的点都是c相同的。所以就与c没什么关系了。
这样我们令sg[i][j]表示i这个点与之前所有点的状态不一样,翻这个点的sg值是多少。这个在求的时候可以暴力枚举所有的后继状态。
有一个问题就是在一个后继状态中会被翻的所有的点得sg值怎么表示?在计算答案的过程中翻一个点会不会对前面的点有影响?
其实这两个是一个问题,就是这个点和它的后继点是否是独立的。
其实是独立的。我们可以看求sg的过程,假如我们翻了这个点后会对前面有些点产生影响,但是对于某一个点来说被翻得总次数是一定的,只是先后顺序不同,所以无论顺序是怎样这个点的状态都会是这个,也就是说这个点可以看成是独立的。

题目链接:

Luogu 4077
BZOJ 4600

Ac 代码:

#include <cstdio>
#include <iostream>
#include <cstring>
int sg[20][20],vis[500],q,n,ans,maxq;
void getsg()
{
    int o,k;
    memset(sg,0,sizeof(sg));
    int x=0,y=0,m;
    m=n;
    while(m>=2) m/=2,x++;
    m=n;
    while(m>=3) m/=3,y++;
     for(int i=0;i<=x;i++)
      for(int j=0;j<=y;j++)
      {
        memset(vis,0,sizeof(vis));
         for(int p=1;p<=i;p++)
          for(int q=1;q<=maxq&&p*q<=i;q++)
          {
            for(o=-1,k=1;k<=q;++k) o=(o==-1)?sg[i-p*k][j]:(o^sg[i-p*k][j]);
            if(o!=-1) vis[o]=true;
          }
         for(int p=1;p<=j;p++)
          for(int q=1;q<=maxq&&p*q<=j;q++)
          {
            for(o=-1,k=1;k<=q;k++) o=(o==-1)?sg[i][j-p*k]:(o^sg[i][j-p*k]);
            if(o!=-1) vis[o]=1;
          }
         for(o=0;;++o)
          if(!vis[o])
          {
            sg[i][j]=o;
            break;
          }
      }
}
int main()
{
    scanf("%d",&q);
    while(q--)
    {
        scanf("%d%d",&n,&maxq);
        getsg();
        int flag=0;
        for(int i=1,x,m;i<=n;i++)
        {
            scanf("%d",&x);
            if(x) continue;
            int s1=0,s2=0;
            m=i;
            while(m%2==0) m/=2,s1++;
            while(m%3==0) m/=3,s2++;
            flag^=sg[s1][s2];
        }
        puts(flag?"win":"lose");
    }

    return 0;
}