BZOJ 4589 Hard Nim
Description
Claris和NanoApe在玩石子游戏,他们有n堆石子,规则如下:
1. Claris和NanoApe两个人轮流拿石子,Claris先拿。
2. 每次只能从一堆中取若干个,可将一堆全取走,但不可不取,拿到最后1颗石子的人获胜。
不同的初始局面,决定了最终的获胜者,有些局面下先拿的Claris会赢,其余的局面Claris会负。
Claris很好奇,如果这n堆石子满足每堆石子的初始数量是不超过m的质数,而且他们都会按照最优策略玩游戏,那么NanoApe能获胜的局面有多少种。
由于答案可能很大,你只需要给出答案对10^9+7取模的值。
Input
输入文件包含多组数据,以EOF为结尾。
对于每组数据:
共一行两个正整数n和m。
每组数据有1<=n<=10^9, 2<=m<=50000。
不超过80组数据。
Output
Sample Input
3 7
4 13
4 13
Sample Output
6
120
120
对于该游戏SG[i]=i
根据SG定理,如果n堆石子的SG异或和为0则后手必胜
问题变成了,从小于m的素数中可重复选n个,异或和为0,有多少方案
当n=2时
可以理解为2个多项式“相乘”
$C[i]=\sum_{j^k=i}A[j]*B[k]$
这是类似与卷积的形式,其实这可以用FWT$O(mlogm)$实现
但不可能我们做n次FWT,因为n最大$10^{9}$
我们发现实际上一次FWT视作一次乘法,就可以看作初始数组$A^{n}$
A[i]=[i是素数]
于是可以快速幂
但实际上没必要在快速幂时用UFWT(逆运算),最后将答案数组UFWT就行了
这样比每次“相乘”时都FWT和UFWT要好了不少,从$O(mlogmlogn)$变成$O(mlogm+mlogn)$
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 bool vis[50001]; 8 int tot,prime[50001],Mod=1e9+7,inv2,n,m; 9 int f[200001],a[200001]; 10 void pre() 11 {int i,j; 12 for (i=2;i<=50000;i++) 13 { 14 if (vis[i]==0) 15 { 16 tot++; 17 prime[tot]=i; 18 } 19 for (j=1;j<=tot;j++) 20 { 21 if (i*prime[j]>50000) break; 22 vis[i*prime[j]]=1; 23 if (i%prime[j]==0) break; 24 } 25 } 26 } 27 int qpow(int x,int y) 28 { 29 int res=1; 30 while (y) 31 { 32 if (y&1) res=1ll*res*x%Mod; 33 x=1ll*x*x%Mod; 34 y>>=1; 35 } 36 return res; 37 } 38 void FWT(int *A,int len) 39 {int i,j,k; 40 for (i=1;i<len;i<<=1) 41 { 42 for (j=0;j<len;j+=(i<<1)) 43 { 44 for (k=0;k<i;k++) 45 { 46 int x=A[j+k],y=A[j+k+i]; 47 A[j+k]=x+y; 48 if (A[j+k]>=Mod) A[j+k]-=Mod; 49 A[j+k+i]=x-y; 50 if (A[j+k+i]<0) A[j+k+i]+=Mod; 51 } 52 } 53 } 54 } 55 void UFWT(int *A,int len) 56 {int i,j,k; 57 for (i=1;i<len;i<<=1) 58 { 59 for (j=0;j<len;j+=(i<<1)) 60 { 61 for (k=0;k<i;k++) 62 { 63 int x=A[j+k],y=A[j+k+i]; 64 A[j+k]=1ll*(x+y)*inv2%Mod; 65 A[j+k+i]=1ll*(x-y+Mod)*inv2%Mod; 66 } 67 } 68 } 69 } 70 int main() 71 {int len,i; 72 inv2=qpow(2,Mod-2); 73 pre(); 74 while (scanf("%d%d",&n,&m)!=EOF) 75 { 76 memset(f,0,sizeof(f)); 77 memset(a,0,sizeof(a)); 78 len=1; 79 while (len<=m) len*=2; 80 f[0]=1; 81 for (i=1;i<=tot;i++) 82 { 83 if (prime[i]>m) break; 84 a[prime[i]]=1; 85 } 86 FWT(f,len);FWT(a,len); 87 while (n) 88 { 89 if (n&1) 90 { 91 for (i=0;i<len;i++) 92 f[i]=1ll*f[i]*a[i]%Mod; 93 } 94 for (i=0;i<len;i++) 95 a[i]=1ll*a[i]*a[i]%Mod; 96 n>>=1; 97 } 98 UFWT(f,len); 99 printf("%d\n",f[0]); 100 } 101 }