bzoj 4589: Hard Nim

传送门

先筛出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

 

posted @ 2018-01-18 22:09  啊宸  阅读(271)  评论(0编辑  收藏  举报