把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【BZOJ4589】Hard Nim(FWT+快速幂)

点此看题面

大致题意: \(nim\)游戏,有\(n\)堆石子,每堆石子个数都是不超过\(m\)的质数,问后手能赢的方案数。

\(nim\)游戏+\(FWT\)

显然,根据\(nim\)游戏的结论,后手能赢意味着石子数异或和为\(0\)

考虑我们令\(p_i\)表示\(i\)是否为质数,假设当前只有两堆石子,可令:

\[f_i=\sum_{j\ xor\ k=i}p_j\times p_k \]

\(f_0\)就是答案。

容易发现,\(j\ xor\ k=i\),恰好是\(FWT\)中异或卷积的条件。

\(n\)堆石子,其实就是\(n\)\(p\)卷在一起,直接快速幂就行了。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define M 50000
#define P 65536
#define X 1000000007
#define I2 500000004
using namespace std;
int n,m,p[P],a[P],t[P];
I void FWT(int *s,CI op)//FWT
{
	for(RI i=1,j,k,x,y;i^P;i<<=1) for(j=0;j^P;j+=i<<1) for(k=0;k^i;++k) x=s[j+k],y=s[i+j+k],
		s[j+k]=(x+y)%X,s[i+j+k]=(x-y+X)%X,op&&(s[j+k]=1LL*s[j+k]*I2%X,s[i+j+k]=1LL*s[i+j+k]*I2%X);
}
I void Mul(int *x,int *y) {for(RI i=0;i^P;++i) x[i]=1LL*x[i]*y[i]%X;}//直接乘
I bool IsP(CI x) {for(RI i=2;i*i<=x;++i) if(!(x%i)) return 0;return 1;}//判断是否为质数
int main()
{
	RI i;for(i=2;i<=M;++i) p[i]=IsP(i);W(scanf("%d%d",&n,&m)==2)//预处理质数表
	{
		for(i=0;i^P;++i) a[i]=0,t[i]=1;for(i=1;i<=m;++i) a[i]=p[i];FWT(a,0);//预处理快速幂数组,并用FWT把它变换掉
		W(n) n&1&&(Mul(t,a),0),Mul(a,a),n>>=1;FWT(t,1),printf("%d\n",t[0]);//快速幂,然后变回来输出
	}return 0;
}
posted @ 2020-06-04 13:20  TheLostWeak  阅读(166)  评论(0编辑  收藏  举报