CF317D Game with Powers sg函数

题目大意:
Vasya V a s y a Petya P e t y a 玩一个游戏,每人每次写一个 x[1,n] x ∈ [ 1 , n ] 的正整数。当 x x 被写后,x x x 的任意正整数幂都不能被写,写不了数字的人输。假设Vasya为先手,问当两边都是最佳操作时,谁能获胜。

分析:
我们可以把一些能用同样底数的幂次分组,即分成 c,c2,c3.... c , c 2 , c 3 . . . . ,为了让分开的集合相互独立, c c 不能表示成其他数(除了c1)的幂形式。既然已经同底数了,所以可以理解为每次可以取一个数 i[1,k] i ∈ [ 1 , k ] ,当选了 i i 时,ij不能选, k k 为满足ck<=n的最大整数,问谁能赢的问题。

因为 n1e9 n ≤ 1 e 9 ,所以 230>1e9 2 30 > 1 e 9 ,所以 k30 k < 30 。对于每一个组,都相当于一个 NIM N I M 游戏,可以用 sg s g 函数求解,再把所有组的 sg s g 异或起来。我们可以打表打出大小为 [1,30] [ 1 , 30 ] 的组的 sg s g 值。

打表代码如下:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <map>

using namespace std;

map <int,int> h;

int a[35],n,g;

int dfs(int x)
{
    if ((x==0) || (h[x])) return h[x];
    bool b[35];
    for (int i=0;i<=n;i++) b[i]=0;
    for (int i=1;i<=n;i++)
    {
        int sub=x;
        if ((x&(1<<(i-1)))!=0)
        {
            for (int j=i;j<=n;j+=i) sub&=g-(1<<(j-1));
            b[dfs(sub)]=1;
        }
    }
    int c=0;
    for (int i=0;i<=n;i++)
    {
        if (!b[i])
        {
            c=i;
            break;
        }
    }
    h[x]=c;
    return c;
}

int main()
{
    for (int i=1;i<=30;i++)
    {   
        n=i;
        g=(1<<n)-1;     
        for (int j=1;j<=i;j++) a[j]=1;
        printf("%d\n",dfs(g));
    }
}

因为大于 n n 的数的 k k 值都是1,所以我们可以暴力前 n n 个数的 sg s g 异或和。但是要排除掉那些可以写成其他数幂次(除 c1 c 1 )的数,然后统计一下 [n+1,n] [ n + 1 , n ] 有多少个这样的数,那么这个范围内剩下的数的 sg s g 值都等于 sg[1] s g [ 1 ] 。因为是异或,只要判断剩下的数的奇偶性即可。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long

const LL sg[31]={0,1,2,1,4,3,2,1,5,6,2,1,8,7,5,9,8,7,3,4,7,4,2,1,10,9,3,6,11,12,14};
const int maxn=1e5+7;

using namespace std;

LL n;
LL ans,v[maxn],t;

int main()
{
    scanf("%lld",&n);
    LL p=trunc(sqrt(n));
    ans=sg[1];  
    for (LL i=2;i<=p;i++)
    {
        if (v[i]) continue;
        LL c=i,k=1;
        while (c*i<=n)
        {
            c*=i;
            if (c<=p) v[c]=1;
                else t++;
            k++;
        }       
        ans^=sg[k];
    }   
    if ((n-p-t)%2==1) ans^=sg[1];
    if (ans==0) printf("Petya");
           else printf("Vasya");
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值