bzoj 4589: Hard Nim fwt

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

分析:
我们设 f [ i ] [ i ] 为有 i 堆石子,异或和为 j 的答案,有

f [ x + y ] [ i ] = j   x o r   k = i f [ x ] [ j ] f [ y ] [ k ]

这是一个异或卷积形式,可以上fwt+快速幂即可。
由于fwt和fft不同的是,卷积的长度是不变的。所以可以直接fwt后直接算出最后的答案,再做逆运算。

代码:

/**************************************************************
    Problem: 4589
    User: liangzihao
    Language: C++
    Result: Accepted
    Time:8212 ms
    Memory:3636 kb
****************************************************************/

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

const int maxn=1e5+7;
const LL mod=1e9+7;
const LL inv2=500000004;

using namespace std;

LL f[maxn],n;
LL prime[maxn],not_prime[maxn];
LL m,cnt,len;

void getprime(LL n)
{
    for (LL i=2;i<=n;i++)
    {
        if (!not_prime[i])
        {
            prime[++cnt]=i;
        }
        for (LL j=1;j<=cnt;j++)
        {
            if (i*prime[j]>n) break;
            not_prime[i*prime[j]]=1;
            if (i%prime[j]==0) break;
        }
    }
}

void fwt(LL *a,LL l,LL r)
{
    if (l==r) return;
    LL n=(r-l+1)/2,mid=n+l;
    fwt(a,l,mid-1);
    fwt(a,mid,r);
    for (LL i=l;i<mid;i++)
    {
        LL u=a[i],v=a[i+n];
        a[i]=(u+v)%mod;
        a[i+n]=(u+mod-v)%mod;
    }
}

void dwt(LL *a,LL l,LL r)
{
    if (l==r) return;
    LL n=(r-l+1)/2,mid=n+l;
    dwt(a,l,mid-1);
    dwt(a,mid,r);
    for (LL i=l;i<mid;i++)
    {
        LL u=a[i],v=a[i+n];
        a[i]=(u+v)%mod*inv2%mod;
        a[i+n]=(u+mod-v)%mod*inv2%mod;
    }
}

LL power(LL x,LL y)
{
    if (y==0) return 1;
    if (y==1) return x;
    LL c=power(x,y/2);
    c=(c*c)%mod;
    if (y%2) c=(c*x)%mod;
    return c;
}

int main()
{
    getprime(5e4);
    while (scanf("%lld%lld",&n,&m)!=EOF)
    {
        len=1;
        while (len<=m) len*=2;
        for (LL i=0;i<len;i++) f[i]=0;               
        for (LL j=1;j<=cnt;j++)
        {
            if (prime[j]<=m) f[prime[j]]=1;
                        else break;
        }                       
        fwt(f,0,len-1);
        for (LL i=0;i<len;i++) f[i]=power(f[i],n%mod);
        dwt(f,0,len-1);
        printf("%lld\n",f[0]);
    }
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/81707587
今日推荐