【博弈论】Day 10 提高组模拟C组 T1 石子游戏

题目大意

给定 n 堆石头,每次可以最多可以取走与该堆石子互质个数量的石子,现两人都以最优策略进行,问谁能获胜

解题思路

首先这道题不存在平局,且都是以最优策略进行的,这类问题我么称为, N I M 尼玛)博弈问题,我们先来看一看该问题的原型

给定 n 件物品,每次可以取走若干件,但不能不取,问先手能否获胜!

这就是 N I M 博弈问题的原型, N I M 博弈有一个定理,那就是

当先手必胜时,当且仅当 a 1     x o r     a 2     x o r     a 3 x o r     a n 1     x o r     a n 0

反之亦然

但是这道题限制了我们最多只能取与其互质的个数,所以就引入了一个新的东西, S G 傻狗)函数, S G 函数可以解决该问题的限制因素,那么具体怎么构建呢?看下面:

S G ( x ) 表示面对有 x 个石头的堆时能取走的个数

x 为合数时,得到 S G ( x ) = S G ( x
x 为质数时,得到 S G ( x ) = m a x { S G ( i ) } + 1 ( i = 1.. x 1 )

接下来,我们可以筛一波素数,在筛素数的过程中计算出每个数的最小质因子,然后令所有石子数变成其对应的 S G 函数,就可以求解了

代码

#include<cstdio>
#define N 1000005
#define Il inline
#define re register
#define max(a,b) a>b?a:b
using namespace std;int prime[N],sg[N],ans,t,n,a;
Il void Make_sg()
{
    prime[1]=1;
    for(int i=2;i<N;i++)
    if(!prime[i])
    {
        prime[i]=i;
        for(int j=i;j<N;j+=i) if(!prime[j]) prime[j]=i;//筛质因数,同时标记每个数的最小质因子
    }
    int maxn=0;
    for(int i=1;i<N;i++)
    {
        if(prime[i]==i) sg[i]=maxn+1;
        else sg[i]=sg[prime[i]];
        maxn=max(maxn,sg[i]);//用maxn来维护最大的SG
    }
}
int main()
{
    Make_sg();
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);ans=0;
        for(re int i=1;i<=n;i++) 
        scanf("%d",&a),ans^=sg[a];//NIM博弈定理
        if(ans) puts("Alice");else puts("Bob");//输出
    }
}

后记

记得当年东莞市赛有一道题,给定 n 堆物品,每次可以取走一堆或其中的一部分,给定先手的人,问谁能赢?

其实这一道题也是一个 N I M 博弈问题,只不过其的 a i 均为1,所以只要谁先手谁就能赢,这也是一个简单的 N I M 博弈问题的应用

该算法时间复杂度为: O ( 1 e 6 l o g l o g 1 e 6 + t n ) O ( N + t n )

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/81055735