Hard Nim,HYSBZ - 4589
https://vjudge.net/problem/HYSBZ-4589/origin
Claris和NanoApe在玩石子游戏,他们有n堆石子,规则如下:
- Claris和NanoApe两个人轮流拿石子,Claris先拿。
- 每次只能从一堆中取若干个,可将一堆全取走,但不可不取,拿到最后1颗石子的人获胜。
不同的初始局面,决定了最终的获胜者,有些局面下先拿的Claris会赢,其余的局面Claris会负。
Claris很好奇,如果这n堆石子满足每堆石子的初始数量是不超过m的质数,而且他们都会按照最优策略玩游戏,那么NanoApe能获胜的局面有多少种。
由于答案可能很大,你只需要给出答案对10^9+7取模的值。
思路: 首先很明显是Nim博弈最经典的问题,要使得后手必胜那么所有数的异或值为0,而每个数都是小于等于m的素数,素数筛一遍得到所有素数,即可得到相应的生成函数,用FWT对自己异或卷积n次然后异或值为0的系数即为答案
#include<bits/stdc++.h>
#define MAXN 50005
#define ll long long
using namespace std;
const int MOD = 1e9+7;
int flag[MAXN],prime[MAXN],num;
inline void euler(int n)
{
for(int i = 2;i <= n;++i)
{
if(!flag[i])
prime[++num] = i;
for(int j = 1;j <= num && i * prime[j] <= n;++j)
{
flag[i * prime[j]] = 1;
if(i % prime[j] == 0)
break;
}
}
}
inline int qpow(int a,int b,int mod)
{
int ans = 1;
while(b)
{
if(b & 1) ans = 1ll * ans * a % mod;
a = 1ll * a * a % mod;
b >>= 1;
}
return ans;
}
int len,inv2;
inline void FWT_xor(int *A,int on)
{
for(int i=1;i<len;i<<=1) for(int p=i<<1,j=0;j<len;j+=p)
for(int k=0;k<i;++k)
{
int x=A[j+k],y=A[i+j+k];
A[j+k]=(x+y)%MOD,A[i+j+k]=(x+MOD-y)%MOD;
if(on==-1) A[j+k]=1ll*A[j+k]*inv2%MOD,A[i+j+k]=1ll*A[i+j+k]*inv2%MOD;//这里是在mol意义下的,否则把inv2改为/2
}
}
int a[MAXN << 1];
int main()
{
inv2 = qpow(2,MOD-2,MOD);
euler(MAXN - 5);
int n,m;
while(~scanf("%d%d",&n,&m))
{
len = 1;
while(len <= m) len <<= 1;
for(int i = 0;i < len;++i) a[i] = 0;
for(int i = 1;i <= num && prime[i] <= m;++i)
a[prime[i]] = 1;
FWT_xor(a,1);
for(int i = 0;i < len;++i)
a[i] = qpow(a[i],n,MOD);
FWT_xor(a,-1);
printf("%d\n",a[0]);
}
return 0;
}