BZOJ 4589: Hard Nim DP+快速幂+FWT

朴素的DP:$f[i][j]$ 表示选了 $i$ 个数,异或值为 $j$ 的方案数.

转移:$f[i][j]=\sum_{i=1}^{m}f[i-1][k]\times isprime[p]$($p$ 异或 $k$ 等于 $j$)

如果 $n$ 比较小的话可以直接进行 FWT 优化 DP.

然而,这道题中 $n=10^9$,那就用快速幂优化 DP 就好啦 ~

 code:

#include <cstdio> 
#include <algorithm>       
#define ll long long  
#define N 100002 
#define MAXM 50000
#define mod 1000000007 
#define setIO(s) freopen(s".in","r",stdin) // ,freopen(s".out","w",stdout)  
using namespace std;                   
int lim,inv,tot; 
int ar[N],br[N],prime[MAXM],vis[MAXM];     
int qpow(int x,int y) 
{
    int tmp=1; 
    for(;y;y>>=1,x=(ll)x*x%mod) 
        if(y&1) tmp=(ll)tmp*x%mod;  
    return tmp; 
}
void FWT(int *f,int opt) 
{            
    int i,j,k;    
    for(i=1;i<lim;i<<=1) 
    {
        for(j=0;j<lim;j+=i<<1)  
            for(k=0;k<i;++k)      
            {
                int x=f[j+k],y=f[j+k+i];         
                f[j+k]=(ll)(x+y)%mod;   
                f[j+k+i]=(ll)(x-y+mod)%mod;   
                if(opt==-1) 
                {
                    f[j+k]=1ll*inv*f[j+k]%mod;   
                    f[j+k+i]=1ll*inv*f[j+k+i]%mod;   
                }
            }
    }
} 
void init()
{                          
    int i,j; 
    for(i=2;i<MAXM;++i) 
    { 
        if(!vis[i]) prime[++tot]=i; 
        for(j=1;j<=tot&&prime[j]*i<MAXM;++j) 
        { 
            vis[i*prime[j]]=1;  
            if(i%prime[j]==0) break;           
        }
    }     
}
void DP(int y) 
{
    for(int i=0;i<lim;++i) br[i]=ar[i];                           
    while(y) 
    {
        if(y&1)
        { 
            for(int i=0;i<lim;++i) ar[i]=(ll)ar[i]*br[i]%mod; 
        }
        y>>=1; 
        for(int i=0;i<lim;++i) 
        {
            br[i]=(ll)br[i]*br[i]%mod;   
        }
    }
}
int main() 
{ 
    // setIO("input");            
    int i,j,n,m;                 
    inv=qpow(2,mod-2),init();       
    while(scanf("%d%d",&n,&m)!=EOF) 
    {    
        for(i=1;i<=tot&&prime[i]<=m;++i) ar[prime[i]]=1;                                         
        for(lim=1;lim<=m;lim<<=1);                     
        FWT(ar,1);         
        DP(n-1);   
        FWT(ar,-1);       
        printf("%d\n",ar[0]);   
        for(i=0;i<lim;++i) ar[i]=br[i]=0; 
    }
    return 0; 
}

  

posted @ 2020-01-09 08:41  EM-LGH  阅读(130)  评论(0编辑  收藏  举报