【XSY】【GZOI2015】石子游戏 博弈论 SG函数

这篇博客介绍了XSY在GZOI2015中遇到的石子游戏问题,玩家可以按规则移除石子,最后取走所有石子者获胜。文章详细讲解了SG函数在解决此类博弈论问题中的应用,并提供了题解思路。通过暴力计算SG值,发现当石子数量为质数时,SG值为该质数在质数序列中的位置加1;否则,SG值等于该数最小质因子的SG值。最后,博主分享了线性筛算法求解问题的代码,时间复杂度为O(a+n)。
摘要由CSDN通过智能技术生成

题目大意

  有 n 堆石子,两个人可以轮流取石子。每次可以选择一堆石子,做出下列的其中一点操作:

  1.移去整堆石子

  2.设石子堆中有x个石子,取出 y 堆石子,其中1y<x (x,y)=1

  取出最后一颗石子的人胜利。问先手胜还是后手胜。

   n100 ,每堆石子个数 ai106

题解

  基础知识:SG函数。

  令 x 为某堆石子的个数。

  SG(i)=mex(SG(j)    ((i,j)=1)

  先用暴力求一遍SG值,然后会发现:

  当 x 为质数时,SG(x)= x 是第几个质数+1。因为前面的质数都可以选,所以就是这个。

  当 x 不是质数时,设y x 的最小质因子,则SG(x)=SG(y)。因为 y 之前的都可以选,y不能选,所以就是 SG(y)

  然后直接线性筛计算答案。

  时间复杂度: O(a+n)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<cmath>
#include<functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
void sort(int &a,int &b)
{
    if(a>b)
        swap(a,b);
}
void open(const char *s)
{
#ifndef ONLINE_JUDGE
    char str[100];
    sprintf(str,"%s.in",s);
    freopen(str,"r",stdin);
    sprintf(str,"%s.out",s);
    freopen(str,"w",stdout);
#endif
}
int rd()
{
    int s=0,c;
    while((c=getchar())<'0'||c>'9');
    do
    {
        s=s*10+c-'0';
    }
    while((c=getchar())>='0'&&c<='9');
    return s;
}
int upmin(int &a,int b)
{
    if(b<a)
    {
        a=b;
        return 1;
    }
    return 0;
}
int upmax(int &a,int b)
{
    if(b>a)
    {
        a=b;
        return 1;
    }
    return 0;
}
int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}
int sg[1000010];
int pri[100010];
int cnt;
int main()
{
    sg[0]=0;
    sg[1]=1;
    int i,j;
    for(i=2;i<=1000000;i++)
    {
        if(!sg[i])
        {
            pri[++cnt]=i;
            sg[i]=cnt+1;
        }
        for(j=1;j<=cnt&&i*pri[j]<=1000000;j++)
        {
            sg[i*pri[j]]=sg[pri[j]];
            if(i%pri[j]==0)
                break;
        }
    }
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,x,s=0;
        scanf("%d",&n);
        for(i=1;i<=n;i++)
        {
            scanf("%d",&x);
            s^=sg[x];
        }
        if(s)
            printf("Alice\n");
        else
            printf("Bob\n");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值