https://www.lydsy.com/JudgeOnline/problem.php?id=4589
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TT61ker1WYwh2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL0MjN2QjMwcTM0ETMwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
思路: N i m Nim Nim博弈先手必败的条件是 n n n堆石子的异或和为 0 0 0,所以本题就是求 n n n个 < = m <=m <=m的异或和为 0 0 0的质数的方案数。显然可以 d p dp dp,设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示前 i i i堆石子异或和为 j j j的方案数,则 d p [ i ] [ j ] = ∑ d p [ i − 1 ] [ j x o r p ] dp[i][j]=∑dp[i-1][j\ xor\ p] dp[i][j]=∑dp[i−1][j xor p],其中 p p p为质数。如果我们有一个数组 v [ i ] v[i] v[i]表示 i i i是否是小于等于 m m m的质数(若 i i i是质数则 v [ i ] = 1 v[i]=1 v[i]=1),则这个转移可以看作是 d p [ i − 1 ] dp[i-1] dp[i−1]和 v v v进行的特殊卷积,再依据 N i m Nim Nim博弈的特点,不难联想到 F W T FWT FWT ,又因为 d p [ 0 ] = v dp[0]=v dp[0]=v,所以对于两堆式子其实就是: F [ i ] = ∑ j x o r k = i v [ j ] ∗ v [ k ] F[i]=\sum_{j\ xor\ k=i}v[j]*v[k] F[i]=j xor k=i∑v[j]∗v[k]
a n s = F [ 0 ] ans=F[0] ans=F[0]
然而现在有 n n n堆石子,暴力做吗?肯定会 T T T。做一次 F W T FWT FWT后取 n n n次方,然后做一次 I F W T IFWT IFWT就可以了。
#include<bits/stdc++.h>
using namespace std; //FWT
typedef long long ll;
const int maxn=5e4+5;
const int MOD=1e9+7;
int n,m,limit,k;
bool vis[maxn];
int prime[maxn];
ll a[maxn<<2];
void euler()
{
vis[1]=1;
int MAX=5e4;
for(int i=2;i<=MAX;i++)
{
if(!vis[i])
prime[++k]=i;
for(int j=1;j<=k&&prime[j]*i<=MAX;j++)
{
vis[prime[j]*i]=1;
if(i%prime[j]==0)
break;
}
}
}
inline ll qpow(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1)
ans=ans*a%MOD;
a=a*a%MOD;
b>>=1;
}
return ans;
}
inline void FWT_xor(ll *A,int inv)
{
ll x,y;
ll inv2=MOD+1>>1; //因为MOD是一个质数 所以(MOD+1)/2 就是2模MOD的乘法逆元
for(int mid=1;mid<limit;mid<<=1)
{
for(int i=0;i<limit;i+=mid<<1)
{
for(int j=0;j<mid;j++)
{
x=A[i+j],y=A[mid+i+j];
A[i+j]=(x+y)%MOD;
A[mid+i+j]=(x-y+MOD)%MOD;
if(inv==-1)
{
A[i+j]=A[i+j]*inv2%MOD; // 2模MOD的乘法逆元 如果题目不是在模意义下的 除2即可
A[mid+i+j]=A[mid+i+j]*inv2%MOD;
}
}
}
}
}
int main()
{
euler();
while(~scanf("%d %d",&n,&m))
{
limit=1;
while(limit<=m)
limit<<=1;
memset(a,0,sizeof(ll)*limit);
for(int i=1;i<=k&&prime[i]<=m;i++)
a[prime[i]]=1;
FWT_xor(a,1);
for(int i=0;i<limit;i++)
a[i]=qpow(a[i],n);
FWT_xor(a,-1);
printf("%lld\n",a[0]);
}
return 0;
}