1. 程式人生 > >bzoj 4589: Hard Nim

bzoj 4589: Hard Nim

一個 ctime 快的 結合 ide 分享圖片 splay img typename

傳送門

先篩出m以內的質數,g[i]表示i是否是素數

f[i][j]表示前n堆數異或和為j的方案數。

f[0][0]=1; f[0][1]~f[0][m]=0;

f[i][j] = sigma( f[i-1][k] * g[k^j] )

發現這個玩意滿足乘法結合律

∴ f[n][] = sigma(f[0][]*g[]*g[]*g[]*g[]……)

=sigma(f[0]*g[]^n) ;

f[0][]為單位元

所以ans=g[]^n

g[]^n可以用FWT+快速冪來算,時間復雜度就是log^2的

然後發現每次FWT變換,相乘,答案變回來,再乘的時候再FWT變換,變回來。。這些步驟是不必要的。

直接FWT然後快速冪,再變換回去就好看,時間復雜度為一個log

(以上感謝llj同學。)

然後就是一個愉快的FWT模板

技術分享圖片
//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<cmath>
#include<ctime>
const int N=100007,M=50000,mod=1e9+7;
typedef long long LL;
using namespace std; int n,m,l,bo[M+5],p[M+5]; LL g[N],inv; template<typename T> void read(T &x) { T f=1; x=0; char ch=getchar(); while(ch!=-&&(ch<0||ch>9)) ch=getchar(); if(ch==-) f=-1,ch=getchar(); for(;ch>=0&&ch<=9;ch=getchar()) x=x*10
+ch-0; x*=f; } void get_prime() { for(int i=2;i<=M;i++) { if(!bo[i]) p[++p[0]]=i; for(int j=1;j<=p[0]&&p[j]*i<=M;j++) { bo[p[j]*i]=1; if(i%p[j]==0) break; } } } LL ksm(LL a,LL b) { LL base=a,res=1; while(b) { if(b&1) res=res*base%mod; base=base*base%mod; b>>=1; } return res; } void FWT(LL a[],int f) { for(int i=1;i<n;i<<=1) for(int q=i<<1,j=0;j<n;j+=q) for(int k=0;k<i;k++) { LL x=a[j+k],y=a[j+k+i]; if(f==1) { a[j+k]=(x+y)%mod; a[j+k+i]=(x-y+mod)%mod;//^ //& a[j+k]=x+y; //| a[j+k+i]=x+y; } else { a[j+k]=(x+y)%mod*inv%mod; a[j+k+i]=(x-y+mod)%mod*inv%mod;//^ //& a[j+k]=x-y; //| a[j+k+i]=y-x; } } } int main() { get_prime(); bo[1]=1; bo[0]=1; inv=ksm(2,mod-2); while(scanf("%d",&n)!=EOF) { read(m); int nn=n; memset(g,0,sizeof(g)); for(int i=0;i<=m;i++) g[i]=(bo[i]==0); l=0; for(n=1;n<=m;n<<=1) l++; FWT(g,1); for(int i=0;i<n;i++) g[i]=ksm(g[i],nn); FWT(g,-1); printf("%lld\n",g[0]); } return 0; } /* 3 7 4 13 */
View Code

bzoj 4589: Hard Nim