背景:
在
luogu
\text{luogu}
luogu上没有找到原题,只好在
bzoj
\text{bzoj}
bzoj老爷机上跑了。
题目传送门:
https://www.lydsy.com/JudgeOnline/problem.php?id=4589
题意:
n
n
n堆石子,每一堆的个数为一个不超过
m
m
m的质数。两个人轮流取,一次只能从一堆中取任意个,求后手赢得方案数。
思路:
据说是
FWT
\text{FWT}
FWT入门题。
做的题少,不会啊。
nim
\text{nim}
nim游戏的性质,若存在异或的结果为
0
0
0,则一定是后手必胜。
其实就是,假设当前的石子个数的序列为
a
a
a,则一定有
a
1
xor
a
2
xor
a
3
xor
.
.
.
xor
a
n
=
0
a_1\text{ xor }a_2\text{ xor }a_3\text{ xor }...\text{ xor }a_n=0
a1 xor a2 xor a3 xor ... xor an=0。
列一个
d
p
dp
dp方程,用
FWT
\text{FWT}
FWT来跑即可。
注意:中间我们可以用快速幂优化一下其中的乘法。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define mod 1000000007
#define inv2 500000004
#define MAXN 200010
using namespace std;
int n,m,len,t=0;
bool bz[MAXN];
int prime[MAXN];
LL a[MAXN],b[MAXN];
void init()
{
bz[0]=bz[1]=true;
for(int i=2;i<=MAXN;i++)
{
if(!bz[i]) prime[++t]=i;
for(int j=1;j<=t&&i*prime[j]<=MAXN;j++)
{
bz[i*prime[j]]=true;
if(!(i%prime[j])) break;
}
}
}
void FWT_xor(LL *f,int op)
{
for(int i=1;i<len;i<<=1)
for(int j=0;j<len;j+=(i<<1))
for(int k=0;k<i;k++)
{
int x=f[j+k],y=f[i+j+k];
f[j+k]=(x+y)%mod;
f[i+j+k]=(x-y+mod)%mod;
if(op==-1)
{
f[j+k]=f[j+k]*inv2%mod;
f[i+j+k]=f[i+j+k]*inv2%mod;
}
}
}
LL dg(LL x,int k)
{
if(!k) return 1;
LL op=dg(x,k>>1);
if(k&1) return op*op%mod*x%mod; else return op*op%mod;
}
int main()
{
init();
while(scanf("%d %d",&n,&m)!=EOF)
{
memset(a,0,sizeof(a));
len=1;
for(;len<=m;len<<=1);
for(int i=1;i<=t&&prime[i]<=m;i++)
a[prime[i]]=1;
FWT_xor(a,1);
for(int i=0;i<len;i++)
a[i]=dg(a[i],n);
FWT_xor(a,-1);
printf("%lld\n",a[0]);
}
}