[bzoj4589] 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组数据。
Sample Input
3 7
4 13
Sample Output
6
120
Solution
转化下题意,就是:
选出\(n\)个小于\(m\)的质数,求使得他们的异或和等于\(0\)的方案数。
构造生成函数:
\[F(x)=\sum_{i=0}^{m}is\_pri(i)\cdot x^i
\]
其中\(is\_pri(i)\)指的是\(i\)是不是质数,是个布尔值。
我们考虑异或卷积,记为\(A\otimes B=C\)。
那么,这个可以写成:
\[C_k=\sum_{i\oplus j=k}A_i\cdot B_j
\]
我们记异或为\(\oplus\)。
然后显然,答案就是:
\[ans_x=\sum_{a_1\oplus a_2 ...\oplus a_n=x}F_{a_1}\cdot F_{a_2}...\cdot F_{a_n}
\]
其中\(ans_x\)表示异或和为\(x\)的方案数。
然后这个东西显然就是\(F\)在异或卷积意义下自乘\(n\)次,直接\(fwt\)优化就行了。
复杂度\(O(m\log n)\)。
#include<bits/stdc++.h>
using namespace std;
void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
const int maxn = 2e5+10;
const int mod = 1e9+7;
const int inv2 = 5e8+4;
int n,a[maxn],m,N,vis[maxn],pri[maxn],tot;
void sieve() {
for(int i=2;i<maxn;i++) {
if(!vis[i]) pri[++tot]=i;
for(int j=1;j<=tot&&i*pri[j]<maxn;j++) {
vis[i*pri[j]]=1;
if(i%pri[j]==0) break;
}
}
}
int qpow(int aa,int x) {
int res=1;
for(;x;x>>=1,aa=1ll*aa*aa%mod) if(x&1) res=1ll*res*aa%mod;
return res;
}
void fwt(int *r,int op) {
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=r[j+k],y=r[i+j+k];
if(op==1) r[j+k]=(x+y)%mod,r[i+j+k]=(x-y)%mod;
else r[j+k]=1ll*(x+y)*inv2%mod,r[i+j+k]=1ll*(x-y)*inv2%mod;
}
}
int main() {
sieve();
while(scanf("%d%d",&n,&m)!=EOF) {
memset(a,0,sizeof a);
for(int i=1;i<=tot&&pri[i]<=m;i++) a[pri[i]]=1;
N=1;while(N<=m) N<<=1;
fwt(a,1);
for(int i=0;i<N;i++) a[i]=qpow(a[i],n);
fwt(a,-1);write((a[0]+mod)%mod);
}
return 0;
}