bzoj 4589 Hard Nim(FWT)

3 篇文章 0 订阅

Description

Claris和NanoApe在玩石子游戏,他们有n堆石子,规则如下:

1. Claris和NanoApe两个人轮流拿石子,Claris先拿。

2. 每次只能从一堆中取若干个,可将一堆全取走,但不可不取,拿到最后1颗石子的人获胜。

不同的初始局面,决定了最终的获胜者,有些局面下先拿的Claris会赢,其余的局面Claris会负。

Claris很好奇,如果这n堆石子满足每堆石子的初始数量是不超过m的质数,而且他们都会按照最优策略玩游戏,那么NanoApe能获胜的局面有多少种。

由于答案可能很大,你只需要给出答案对10^9+7取模的值。

Input

输入文件包含多组数据,以EOF为结尾。

对于每组数据:

共一行两个正整数n和m。

每组数据有1<=n<=10^9, 2<=m<=50000。

不超过80组数据。

Output

 

Sample Input

3 7
4 13

Sample Output

6
120

思路:NanoApe能获胜最后是抑或值为0的情况。先筛出M内的素数,把符合条件的素数的a置为1,然后最终要选N个数,联想到fwt,就是相当于求N个a数组的的抑或卷积为0的情况有多少种。

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn=1e5+100;
const int mod=1e9+7;
int prime[maxn+5];
void getPrime()
{
    memset(prime,0,sizeof(prime));
    for(int i=2;i<=maxn;i++)
    {
        if(!prime[i])prime[++prime[0]]=i;
        for(int j=1;j<=prime[0]&&prime[j]<=maxn/i;j++)
        {
            prime[prime[j]*i]=1;
            if(i%prime[j]==0) break;
        }
    }
}
ll qmod(ll x,ll p)
{
    ll ans=1;
    while(p)
    {
        if(p&1)
            ans=ans*x%mod;
        x=x*x%mod;
        p>>=1;
    }
    return ans;
}
int rev=mod+1>>1;
void FWT(int a[],int n)
{
    for(int d=1;d<n;d<<=1)
        for(int m=d<<1,i=0;i<n;i+=m)
            for(int j=0;j<d;j++)
            {
                int x=a[i+j],y=a[i+j+d];
                a[i+j]=(x+y)%mod,a[i+j+d]=(x-y+mod)%mod;
            }
}

void UFWT(int a[],int n)
{
    for(int d=1;d<n;d<<=1)
        for(int m=d<<1,i=0;i<n;i+=m)
            for(int j=0;j<d;j++)
            {
                int x=a[i+j],y=a[i+j+d];
                a[i+j]=1LL*(x+y)*rev%mod,a[i+j+d]=(1LL*(x-y)*rev%mod+mod)%mod;
            }
}
int N,M;
void solve(int a[],int n)
{
    FWT(a,n);
    for(int i=0;i<n;i++)
        a[i]=qmod(a[i],N);
    UFWT(a,n);
}
int n,a[maxn];
int main()
{
    getPrime();
    while(~scanf("%d%d",&N,&M))
    {
        memset(a,0,sizeof a);
        for(int i=1;prime[i]<=M;i++)
            a[prime[i]]=1;
        n=1;
        while(n<=M) n<<=1;
        solve(a,n);
        printf("%d\n",a[0]);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值