[bzoj4589]Hard Nim【FWT】

【题目链接】
  http://www.lydsy.com/JudgeOnline/problem.php?id=4589
【题解】
  就是求 n n 堆石子异或和为0的方案数。
  这个东西显然满足结合律,因此倍增计算就行了。暴力的复杂度 O(m2log2n) O ( m 2 ∗ l o g 2 n )
  考虑用FWT优化转移,这是经典的xor卷积。
  复杂度 O(mlog2mlog2n) O ( m l o g 2 m l o g 2 n )

/* --------------
    user Vanisher
    problem bzoj-4589 
----------------*/
# include <bits/stdc++.h>
# define    ll      long long
# define    inf     0x3f3f3f3f
# define    M       100010
# define    N       100010
# define    P       1000000007
using namespace std;
ll read(){
    ll tmp=0, fh=1; char ch=getchar();
    while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
    while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
    return tmp*fh;
}
ll mypow(ll x, ll y){
    ll i=x; x=1;
    while (y>0){
        if (y%2==1) x=x*i%P;
        i=i*i%P;
        y/=2;
    }
    return x;
}
ll rev=mypow(2,P-2),j[N],f[M+10],p[M+10],pnum,n,m,a[N],l;
void FWT(ll *a, ll l){
    for(ll d=1; d<l; d<<=1)  
        for(ll m=(d<<1),i=0; i<l; i+=m)  
            for(ll j=0;j<d;j++)  
            {  
                ll x=a[i+j],y=a[i+j+d];  
                a[i+j]=(x+y)%P,a[i+j+d]=(x-y+P)%P;  
            }
}
void UFWT(ll *a, ll l){
    for(ll d=1; d<l; d<<=1)  
        for(ll m=(d<<1),i=0; i<l; i+=m)  
            for(ll j=0;j<d;j++)  
            {  
                ll x=a[i+j],y=a[i+j+d];  
                a[i+j]=(x+y)*rev%P,a[i+j+d]=((x-y)*rev%P+P)%P;  
            }
}
void mypow(ll *x, ll y, ll l){
    for (ll i=0; i<l; i++) j[i]=x[i];
    for (ll i=0; i<l; i++) x[i]=1;
    while (y>0){
        if (y%2==1) for (ll i=0; i<l; i++) x[i]=(x[i]*j[i])%P;
        for (ll i=0; i<l; i++) j[i]=(j[i]*j[i])%P;
        y/=2;
    }
}
void getp(){
    f[1]=true;
    for (ll i=2; i<=M; i++)
        if (f[i]==false){
            p[++pnum]=i;
            for (ll j=i+i; j<=M; j+=i)
                f[j]=true;
        }
}
int main(){
    getp();
    while (scanf("%lld%lld",&n,&m)!=EOF){
        memset(a,0,sizeof(a));
        for (ll i=1; p[i]<=m; i++) a[p[i]]=1;
        l=1; while (l<=m) l<<=1;
        FWT(a,l);
        mypow(a,n,l);
        UFWT(a,l);
        printf("%lld\n",a[0]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值