【JZOJ4024】【佛山市选2015】石子游戏(SG函数)

Problem

  本题有T组测试数据。
  给出N堆石子。A 和 B 轮流操作,A 先手。操作者在每一轮中必须选择一堆石子,并且作出下列两种操作中的一种:
  (1)移走整堆石子
  (2)设这堆石子中有 N 个,你可以从中取出 Y 个石子,若 Y 满足与N互质。
  取走最后一个石子的人胜出。若 A 和 B 都以最优策略执行,对于每组数据,询问最后谁会胜利。

Hint

  20%的数据,N<=5,每堆石子数量少于10
  100%的数据,T<=100,N<=100,每堆石子数量不大于1,000,000

Solution

  这道题在JZOJ里面有问题,(2)操作中少了“Y与N互质”这个条件,但是我比赛时还是不会做,只打了 O ( 10 6 N ) 的DP。
  那么先考虑如果(2)操作中没有“Y与N互质”这个条件应该怎么做。

SG函数

  对于每一堆石子,我们可设SG(i)=mex({SG(j)}),其中j为i的一种转移,而mex表示集合中最小没有出现过的自然数。设边界条件SG(0)=0。这样的话,SG(x)=x,且当SG(x)为0时,x为必败态P-position(即先手必败),否则为必胜态N-position(因为只有一堆)。
  那么对于多堆呢?难道我们要用SG(i)中的i去表示一种场面,也即由多堆石子构成的一个集合吗?这样转移起来显然复杂度堪忧。

SG定理

  这时我们就需要用到:Sprague-Grundy定理(SG定理):
  游戏和的SG函数等于各个游戏SG函数的Nim和。这样就可以将每一个子游戏分而治之,从而简化了问题。
  那么对于这个问题,SG(X)(X为一个集合)=SG(x1)^SG(x2)^SG(x3)^…^SG(xn)(xi∈X)。其实这个结论我以前就知道,只不过不会证明。
  但这个定理的证明却也不复杂,基本上就是按照两种position的证明来的。
  根据定义,证明一种判断position的性质的方法的正确性,只需证明三个命题: 1、这个判断将所有无法进行任何移动的局面(也就是terminal position)判为P-position;2、根据这个判断被判为N-position的局面一定可以移动到某个P-position;3、根据这个判断被判为P-position的局面无法移动到某个P-position。
  对于第一个命题,treminal position肯定是{0,0,0,…,0},那么异或和肯定也为0。
  对于第二个命题,N-position即为异或和不为0的状态,设它的异或和为S,那么若x为S在二进制下的最高位,则肯定有一堆石子的数量≥ 2 x (不然它那个x怎么来的),设这堆的数量为x1。又因为x1>S^x1,所以我们先把x1拎出来,使S变成两个子状态x1和S^x1,再将x1取剩S^x1,然后再合并为0。举例来说,若S为 ( 011011 ) 2 ,x1为 ( 010110 ) 2 ,则S^x1为 ( 001101 ) ,将x1取剩S^x1后,S则为 ( 000000 ) 2
  对于第三个命题,你取石子肯定会改变它的异或和,所以你没办法把异或和从0变成0。
  
  于是再回到这道题上。我们可以近似地设SG(i)=mex({SG(i-j)}(i,j)=1∪{0}),那由于(i,j)=1,(i,i-j)肯定也=1,所以SG(i)=mex({SG(j)}(i,j)=1∪{0})。SG(i)随i的变化而变化的规律如下表:

i SG(i)
0 0
1 1
2 2
3 3
4 2
5 4
6 2
7 5
8 2
9 3
10 2

  我们可以发现,对于自然数i,设它的最小质因数为k,则SG(i)=rank(k)(k在质数表中的序号)+1。为什么会这样呢?
  因为0~3的SG(i)=i。但在3以后,i若是质数,则会产生一个新的SG(i);i若是合数,则SG(i)=mex({SG(j)}(i,j)=1∪{0}),也就是说SG(i)肯定不为那些与它不互质的那些j的SG值,而质数的SG又是单调递增的,所以SG(i)肯定=SG(k)。
  这样,我们就相当于把石子堆数转化成了SG的值,然后再去做普通的取石子游戏。
  那么,这样设SG函数为什么是对的呢?我们可以再去证明一下那三个命题。
  对于第一个命题,treminal position的SG肯定是{0,0,0,…,0},那么异或和肯定也为0。
  对于第二个命题,N-position即为SG异或和不为0的状态,设SG的异或和为S,那么若x为S在二进制下的最高位,则肯定有一堆石子的SG≥ 2 x ,设这堆的SG为x1。那么它肯定可以把自己的SG的值转移成一个比自己小的SG值,因为它是用mex求的。可以举个栗子:这堆的石子数为15,SG为3;它若把SG转移给2,则它可能可以将石子数转移成6,那样看似非法,因为虽然它们的最小质因数不等,但(15,6)=3;但实际上,(15,2)肯定=1,不然SG(15)就=SG(2)了,所以我们可以视为它把石子数转移成了2,SG值也转移成了2。
  对于第三个命题,虽然它不同于普通的取石子游戏,它的SG值可能会转移成比它本身大的SG值;但是它肯定不能转移成与它相等的SG值,因为如果有两个数的SG值相等,那它们的最小质因数也相等,所以它们肯定不互质;所以每次操作都会使一堆的SG值变化,而这样肯定不能使SG值的异或和保持为0。
  于是此法得证。
  对于SG,我们可以线筛一遍,顺便求出每个数的SG。
  时间复杂度:预处理 O ( 1000000 ) ,计算答案 O ( T N )

Code

#include <cstdio>
#include <cstring>
using namespace std;
#define fo(i,a,b) for(i=a;i<=b;i++)
const int A=1e6;
int i,j,GS[A+1],cnt,price[A],t,n,x,ans;
bool p[A+1];
void init()
{
    memset(p,1,sizeof p);
    GS[1]=1;
    fo(i,2,A)
    {
        if(p[i])GS[price[++cnt]=i]=cnt+1;
        for(j=1;j<=cnt&&(j==1||i%x)&&i*(x=price[j])<=A;++j)
        {
            GS[i*x]=j+1;
            p[i*x]=0;
        }
    }
}
void work()
{
    for(scanf("%d",&t);t;t--)
    {
        scanf("%d",&n);
        ans=0;
        fo(i,1,n)
        {
            scanf("%d",&x);
            ans^=GS[x];
        }
        printf(ans?"Alice\n":"Bob\n");
    }
}
int main()
{
    init();
    work();
}

猜你喜欢

转载自blog.csdn.net/qq_36551189/article/details/79871626