bzoj 4589 Hard Nim —— FWT
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4589
先手必败,是一开始所有石子的异或和为0;
生成函数 (xpri[1] + xpri[2] + ... + xpri[k])n,pri[k] <= m
FWT求解即可;
而且不要快速幂里面每次变换来变换去的,只有快速幂前后需要变换。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; int const xn=(1<<16),mod=1e9+7; int n,m,a[xn],b[xn],lim,inv,cnt,pri[xn]; bool vis[xn]; void init() { int mx=xn-1; for(int i=2;i<=mx;i++) { if(!vis[i])pri[++cnt]=i; for(int j=1;j<=cnt&&(ll)i*pri[j]<=mx;j++) { vis[i*pri[j]]=1; if(i%pri[j]==0)break; } } } int upt(int x){while(x>=mod)x-=mod; while(x<0)x+=mod; return x;} ll pw(ll a,int b) { ll ret=1; for(;b;b>>=1,a=(a*a)%mod)if(b&1)ret=(ret*a)%mod; return ret; } void fwt(int *a,int tp) { for(int mid=1;mid<lim;mid<<=1) for(int j=0,len=(mid<<1);j<lim;j+=len) for(int k=0;k<mid;k++) { int x=a[j+k],y=a[j+mid+k]; a[j+k]=upt(x+y); a[j+mid+k]=upt(x-y); if(tp==-1)a[j+k]=(ll)a[j+k]*inv%mod,a[j+mid+k]=(ll)a[j+mid+k]*inv%mod; } } int main() { inv=pw(2,mod-2); init(); while(scanf("%d%d",&n,&m)==2) { memset(a,0,sizeof a); for(int i=2;i<=m;i++)if(!vis[i])a[i]=1; memset(b,0,sizeof b); b[0]=1; lim=1; while(lim<=m)lim<<=1; fwt(a,1); fwt(b,1); for(;n;n>>=1) { if(n&1)for(int i=0;i<lim;i++)b[i]=(ll)a[i]*b[i]%mod; for(int i=0;i<lim;i++)a[i]=(ll)a[i]*a[i]%mod; } fwt(b,-1); printf("%d\n",b[0]); } return 0; }