由nim游戏的结论可知,就是n堆的数目的亦或和为0时,后手必胜。
考虑昨天学的fwt,我们先预处理出有哪些<=m的质数,然后放到初始数组里面使它的频数为1。ans数组的初始值就是0
所以对n进行快速幂。然而fwt有个性质,就是fwt2个数组以后,要求f()^n次幂,就是直接把fwt后的数组^n次幂,最后再ufwt回来,得到的答案就是^n幂次后的。不需要每次都对ans和tmp数组乘幂都fwt,ufwt一遍,复杂度会多个logT掉。
考场上不知道这个性质,T到怀疑人生。开心地爆零了。(好多数据范围好小的水题都以为是大数据结构题没写,好气啊。
#include<cstdio>
#include<cstring>
#define maxl (1<<18)+5
#define mod 1000000007
int n,m,mm=0,rev;
int p[maxl];
int ans[maxl],tmp[maxl];
bool no[maxl];
void shai()
{
int t,j;
no[1]=true;
for(int i=2;i<maxl;i++)
{
if(!no[i])
p[++p[0]]=i;
t=p[1]*i;j=1;
while(j<=p[0] && t<maxl)
{
no[t]=true;
if(i%p[j]==0)
break;
t=i*p[++j];
}
}
}
inline long long qp(long long a,long long b)
{
long long ans=1,cnt=a;
while(b)
{
if(b&1)
ans=(ans*cnt)%mod;
cnt=(cnt*cnt)%mod;
b>>=1;
}
return ans;
}
inline void prework()
{
for(int i=0;i<mm;i++)
ans[i]=0,tmp[i]=0;
for(int i=1;i<=p[0];i++)
{
if(p[i]>m)
break;
tmp[p[i]]++;
}
for(int i=1;i<=30;i++)
if((1<<i)>m)
{
mm=1<<i;
break;
}
}
inline void fwt(int a[],int n)
{
for(int d=1;d<n;d<<=1)
for(int m=d<<1,i=0;i<n;i+=m)
for(int j=0;j<d;j++)
{
int x=a[i+j],y=a[i+j+d];
a[i+j]=(x+y)%mod,a[i+j+d]=(x-y+mod)%mod;
}
}
inline void ufwt(int a[],int n)
{
for(int d=1;d<n;d<<=1)
for(int m=d<<1,i=0;i<n;i+=m)
for(int j=0;j<d;j++)
{
int x=a[i+j],y=a[i+j+d];
a[i+j]=(1LL*(x+y)*rev)%mod,a[i+j+d]=(1LL*(x-y)*rev%mod+mod)%mod;
}
}
inline void solve(int a[],int b[],int n)
{
fwt(a,n);fwt(b,n);
for(int i=0;i<n;i++)
a[i]=(1LL*a[i]*b[i])%mod;
ufwt(a,n);
}
inline void mainwork()
{
int d=1;
ans[0]=1;
fwt(ans,mm);fwt(tmp,mm);
while(n)
{
if(n&1)
for(int i=0;i<mm;i++)
ans[i]=(1LL*ans[i]*tmp[i])%mod;
for(int i=0;i<mm;i++)
tmp[i]=(1LL*tmp[i]*tmp[i])%mod;
n>>=1;
}
ufwt(ans,mm);
}
inline void print()
{
printf("%d\n",ans[0]);
}
int main()
{
shai();rev=qp(2,mod-2);
while(~scanf("%d%d",&n,&m))
{
prework();
mainwork();
print();
}
return 0;
}