题目描述
有 $n$ 堆石子,每堆石子是不超过 $m$ 的质数,求有多少种局面,使 $Nim$ 游戏中先手获胜
数据范围
$n le 10^9,m le 50000$
题解
首先我们知道 $Nim$ 游戏中先手获胜的条件是 $n$ 堆石子异或值为 $0$
于是我们就 $Fwt$ +快速幂求出第 $0$ 项的值即可
效率: $O(mlog(nm))$
代码
#include <bits/stdc++.h> using namespace std; const int N=1<<17,P=1e9+7,i2=(P+1)>>1; int n,m,t,p[N],c,a[N],b[N];bool F[N]; int X(int x){return x>=P?x-P:x;} void Fwt(int *g,int o){ for (int i=1;i<t;i<<=1) for (int j=0;j<t;j+=(i<<1)) for (int x,y,k=0;k<i;k++){ x=g[j+k];y=g[i+j+k]; g[j+k]=X(x+y);g[i+j+k]=X(x-y+P); if (!o) g[j+k]=1ll*g[j+k]*i2%P, g[i+j+k]=1ll*g[i+j+k]*i2%P; } } int main(){ for (int i=2;i<N;i++){ if (!F[i]) p[++c]=i; for (int v,j=1;j<=c;j++){ v=p[j]*i; if (v>=N) break; F[v]=1; if (i%p[j]==0) break; } } while(~scanf("%d%d",&n,&m)){ for (int j=1;j<=c;j++) if (p[j]<=m) a[p[j]]=1; for (t=1;t<=m;t<<=1);b[0]=1; Fwt(a,1);Fwt(b,1); while(n){ if (n&1) for (int i=0;i<t;i++) b[i]=1ll*b[i]*a[i]%P; for (int i=0;i<t;i++) a[i]=1ll*a[i]*a[i]%P;n>>=1; } Fwt(b,0);printf("%d ",b[0]); for (int i=0;i<t;i++) a[i]=b[i]=0; } return 0; }