传送门
说真的,以后还是要用(cn.vjudge.net),(vjudge.net)直接让我在这道题上Submit Failed了六次,用国内的服务器丢包率真的低好多。。。
题解:
根据Nim游戏的性质我们知道,先手必败当且仅当所有堆的石子数的异或和为 0 0 0。
换句话说,我们需要求在 L L L以内的质数中允许重复地,不考虑顺序地选择 M M M个出来,使得异或和为 0 0 0的方案数。
建立集合幂级数OGF,相当于求 M M M次异或卷积,然后算出点值直接算 M M M次方就行了。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
cs int mod=1e9+7,inv2=mod+1>>1;
inline int add(int a,int b){return (a+=b)>=mod?a-mod:a;}
inline int dec(int a,int b){return (a-=b)<0?a+mod:a;}
inline int mul(int a,int b){static ll r;r=(ll)a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b,int res=1){
for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));
return res;
}
inline void Inc(int &a,int b){(a+=b)>=mod&&(a-=mod);}
inline void Dec(int &a,int b){(a-=b)<0&&(a+=mod);}
class Nim{
private:
static cs int P=5e4+7;
bool mark[P];
std::vector<int> p;
void linear_sieves(int lim){
memset(mark,0,sizeof mark);
for(int re i=2;i<=lim;++i){
if(!mark[i])p.push_back(i);
for(int re j:p){
if(i*j>lim)break;
mark[i*j]=true;
if(i%j==0)break;
}
}
}
static cs int SIZE=1<<16|1;
int a[SIZE];
void FWT(int *A,int len,int typ){
for(int re i=1;i<len;i<<=1)
for(int re j=0;j<len;j+=i<<1)
for(int re k=0;k<i;++k){
int x=A[j+k],y=A[i+j+k];
A[j+k]=add(x,y),A[i+j+k]=dec(x,y);
}
if(typ==-1)for(int re inv=power(inv2,__builtin_ctz(len)),i=0;i<len;++i)A[i]=mul(A[i],inv);
}
public:
Nim(){}
int count(int K,int L){
linear_sieves(L);
int l=1;while(l<=p.back())l<<=1;
memset(a,0,l<<2);
for(int re v:p)a[v]=1;
FWT(a,l,1);
for(int re i=0;i<l;++i)a[i]=power(a[i],K);
FWT(a,l,-1);
return a[0];
}
};
#ifdef zxyoi
Nim Solver;
signed main(){
// freopen("nim.in","r",stdin);
int K,L;
std::cin>>K>>L;
std::cout<<Solver.count(K,L);
return 0;
}
#endif