Description
Claris和NanoApe在玩石子游戏,他们有n堆石子,规则如下:
- Claris和NanoApe两个人轮流拿石子,Claris先拿。
- 每次只能从一堆中取若干个,可将一堆全取走,但不可不取,拿到最后1颗石子的人获胜。
不同的初始局面,决定了最终的获胜者,有些局面下先拿的Claris会赢,其余的局面Claris会负。
Claris很好奇,如果这n堆石子满足每堆石子的初始数量是不超过m的质数,而且他们都会按照最优策略玩游戏,那么NanoApe能获胜的局面有多少种。
由于答案可能很大,你只需要给出答案对10^9+7取模的值。
Solution
这是普通的 (nim) 游戏,异或和为 (0) 即先手必败,那么就可以 (DP) 求解了
设 (f[i][j]) 表示前 (i) 堆,异或和为 (j) 的方案数
那么 (f[i][j]=sum f[i-1][p]*f[i-1][q]\,\,p⊕q=j)
直接 (FWT) 优化一下就好了
(n) 比较大,用快速幂做一下就行了,注意 (FWT) 要写在快速幂外面,快速幂中做点值运算复杂度才对
#include<bits/stdc++.h>
using namespace std;
const int N=100010,mod=1e9+7,inv=500000004;
int k,n,m;bool vis[N];
inline void priwork(){
for(int i=2;i*i<=50000;i++){
if(vis[i])continue;
for(int j=i*i;j<=50000;j+=i)vis[j]=1;
}vis[0]=vis[1]=1;
}
inline void fwt(int *A,int o){
for(int i=1;i<n;i<<=1)
for(int j=0;j<n;j+=i<<1)
for(int k=0;k<i;k++){
int x=A[j+k],y=A[j+k+i];
if(!o)A[j+k]=(x+y)%mod,A[j+k+i]=(x-y+mod)%mod;
else A[j+k]=1ll*(x+y)*inv%mod,A[j+k+i]=1ll*(x-y+mod)*inv%mod;
}
}
int a[N],b[N];
inline void work(){
for(n=1;n<=m;n<<=1);
for(int i=0;i<=m;i++)a[i]=vis[i]^1;
b[0]=1;
fwt(a,0);fwt(b,0);
while(k){
if(k&1)for(int i=0;i<=n;i++)b[i]=1ll*b[i]*a[i]%mod;
for(int i=0;i<=n;i++)a[i]=1ll*a[i]*a[i]%mod;k>>=1;
}
fwt(b,1);
printf("%d
",b[0]);
for(int i=0;i<=n;i++)a[i]=b[i]=0;
}
int main(){
freopen("pp.in","r",stdin);
freopen("pp.out","w",stdout);
priwork();
while(~scanf("%d%d",&k,&m))work();
return 0;
}