题意
Claris和NanoApe在玩石子游戏,他们有n堆石子,规则如下:
1. Claris和NanoApe两个人轮流拿石子,Claris先拿。
2. 每次只能从一堆中取若干个,可将一堆全取走,但不可不取,拿到最后1颗石子的人获胜。
不同的初始局面,决定了最终的获胜者,有些局面下先拿的Claris会赢,其余的局面Claris会负。
Claris很好奇,如果这n堆石子满足每堆石子的初始数量是不超过m的质数,而且他们都会按照最优策略玩游戏,那么NanoApe能获胜的局面有多少种。
由于答案可能很大,你只需要给出答案对10^9+7取模的值。
1<=n<=10^9, 2<=m<=50000。
分析
fwt模板题但我没想到。
首先把这个序列的生成函数求出来,如果x是质数则x次项的系数为1反之则为0。然后我们对这个多项式做n次异或卷积后,0次项的系数即为答案。
异或卷积的话,可以先fwt一下,再把自己n次方一下,最后dwt回去即可。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=100005;
const int MOD=1000000007;
const int ny2=500000004;
int n,m,prime[N],tot,a[N],tmp[N];
bool not_prime[N];
void get_prime(int n)
{
not_prime[1]=not_prime[0]=1;
for (int i=2;i<=n;i++)
{
if (!not_prime[i]) prime[++tot]=i;
for (int j=1;j<=tot&&i*prime[j]<=n;j++)
{
not_prime[i*prime[j]]=1;
if (i%prime[j]==0) break;
}
}
}
int ksm(int x,int y)
{
int ans=1;
while (y)
{
if (y&1) ans=(LL)ans*x%MOD;
x=(LL)x*x%MOD;y>>=1;
}
return ans;
}
void fwt(int *a,int l,int r)
{
if (l==r) return;
int n=(r-l+1)/2,mid=l+n-1;
fwt(a,l,mid);fwt(a,mid+1,r);
for (int i=l;i<=mid;i++)
{
int x=a[i],y=a[i+n];
a[i]=x+y;a[i]-=a[i]>=MOD?MOD:0;
a[i+n]=x-y+MOD;a[i+n]-=a[i+n]>=MOD?MOD:0;
}
}
void dwt(int *a,int l,int r)
{
if (l==r) return;
int n=(r-l+1)/2,mid=l+n-1;
dwt(a,l,mid);dwt(a,mid+1,r);
for (int i=l;i<=mid;i++)
{
int x=a[i],y=a[i+n];
a[i]=(LL)(x+y)*ny2%MOD;
a[i+n]=(LL)(x-y+MOD)*ny2%MOD;
}
}
int main()
{
get_prime(50000);
while (scanf("%d%d",&n,&m)!=EOF)
{
int len;
for (len=1;len<=m;len<<=1);
for (int i=0;i<len;i++) a[i]=0;
for (int i=1;i<=tot;i++) if (prime[i]<=m) a[prime[i]]=1;
fwt(a,0,len-1);
for (int i=0;i<len;i++) a[i]=ksm(a[i],n);
dwt(a,0,len-1);
printf("%d\n",a[0]);
}
return 0;
}