【bzoj4589】Hard Nim FWT+快速幂

题目大意:给你$n$个不大于$m$的质数,求有多少种方案,使得这$n$个数的异或和为$0$。其中,$n≤10^9,m≤10^5$。

 

考虑正常地dp,我们用$f[i][j]$表示前$i$个数的异或和为$j$的方案数。

我们构造一个数组$g$,若i为不大于$m$的质数,则$g[i]=1$,否则为$0$。

那么显然,$f[i][j]=\sum f[i-1][k]\times g[j \oplus k]$。  其中$j \oplus k$表示$j$和$k$的按位异或。

然后我们不难发现,$f[i]为f[i-1]$与$g$的异或卷积。

则$f[n]$为$g$的$n$次异或卷积,答案显然为$f[n][0]$。

我们用$FWT$将$g$变成点值表达式,然后做快速幂,最后再插值回来,就得到答案了。

时间复杂度为$O(m\ log\ m+m\ log\ n)$。

 

 

 1 #include<bits/stdc++.h>
 2 #define M 131072
 3 #define L long long
 4 #define MOD 1000000007
 5 using namespace std;
 6 int b[M]={0},pri[M]={0},use=0;
 7 void init(){
 8     for(int i=2;i<M;i++){
 9         if(!b[i]) pri[++use]=i;
10         for(int j=1;j<=use&&i*pri[j]<M;j++){
11             b[i*pri[j]]=1;
12             if(i%pri[j]==0) break;
13         }
14     }
15 }
16 L pow_mod(L x,int k){
17     L ans=1;
18     while(k){
19         if(k&1) ans=ans*x%MOD;
20         x=x*x%MOD; k>>=1;
21     }
22     return ans;
23 }
24 void FWT(L a[],int n,int on){
25     for(int i=1;i<n;i<<=1)
26     for(int j=0;j<n;j++)
27     if(i&j){
28         L w=a[i^j];
29         a[i^j]=(w+a[j])%MOD;
30         a[j]=(w-a[j]+MOD)%MOD;
31     }
32     if(on==-1){
33         L inv=pow_mod(n,MOD-2);
34         for(int i=0;i<n;i++) a[i]=a[i]*inv%MOD;
35     }
36 }
37 L g[M]={0},ans[M]={0};
38 int main(){
39     init();
40     int t,n;
41     while(cin>>t>>n){
42         int len=1; while(len<=n) len<<=1;
43         for(int i=2;i<=n;i++) if(b[i]==0) g[i]=1;
44         FWT(g,len,1); memcpy(ans,g,M<<3); t--;
45         while(t){
46             if(t&1) for(int i=0;i<len;i++) ans[i]=ans[i]*g[i]%MOD;
47             t>>=1; for(int i=0;i<len;i++) g[i]=g[i]*g[i]%MOD;
48         }
49         FWT(ans,len,-1);
50         printf("%lld\n",ans[0]);
51         memset(g,0,len<<3); memset(ans,0,len<<3);
52     }
53 }

 

 

 

 

posted @ 2018-06-29 09:25  AlphaInf  阅读(175)  评论(0编辑  收藏