bzoj 4589: Hard Nim【线性筛+FWT+快速幂】

T了两次之后我突然意识到转成fwt形式之后,直接快速幂每次乘一下最后再逆回来即可,并不需要没此次都正反转化一次……
就是根据nim的性质,先手必输是所有堆个数异或和为0,也就变成了一个裸的板子

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=500005,mod=1e9+7,inv2=500000004;
int n,m,q[N],tot,bt,lm;
long long a[N],c[N],r[N];
bool v[N];
void dft(long long a[],int f)
{
	for(int i=1;i<lm;i<<=1)
		for(int j=0;j<lm;j+=(i<<1))
			for(int k=0;k<i;k++)
			{
				long long x=a[j+k],y=a[i+j+k];
				a[j+k]=(x+y)%mod,a[i+j+k]=(x-y+mod)%mod;
				if(f==-1)
					a[i+j+k]=a[i+j+k]*inv2%mod,a[j+k]=a[j+k]*inv2%mod;
			}
}
int main()
{
	v[1]=1;
	for(int i=2;i<=50000;i++)
	{
		if(!v[i])
			q[++tot]=i;
		for(int j=1;j<=tot&&i*q[j]<=50000;j++)
		{
			v[i*q[j]]=1;
			if(i%q[j]==0)
				break;
		}
	}
	while(~scanf("%d%d",&n,&m))
	{
		memset(a,0,sizeof(a));
		memset(r,0,sizeof(r));
		bt=0,lm=0;
		for(int i=1;i<=tot&&q[i]<=m;i++)
			a[q[i]]=1;
		for(;(1<<bt)<=m;bt++);
		lm=(1<<bt);
		n--;
		dft(a,1);
		for(int i=0;i<lm;i++)
			r[i]=a[i];
		while(n)
		{
			if(n&1)
			{
				for(int i=0;i<lm;i++)
					r[i]=r[i]*a[i]%mod;
			}
			for(int i=0;i<lm;i++)
				a[i]=a[i]*a[i]%mod;
			n>>=1;
		}
		dft(r,-1);
		printf("%lld\n",r[0]);
	}
	return 0;
}
posted @ 2018-11-30 16:11  lokiii  阅读(185)  评论(0编辑  收藏  举报