BZOJ4589: Hard Nim【FWT+DP】

4589: Hard Nim

【题目描述】

传送门

【题解】

长度为n的不大于m的素数序列亦或和为0的方案数。

考虑DP:F[i][xF[i][x^y]=F[i1][x]F[1][y]y]=F[i-1][x]*F[1][y]

显然F[1][i]F[1][i]只有ii为素数是F[1][i]=1F[1][i]=1其余等于0。

对于以上式子,就是一个典型的FWT形式。

先对F[1]F[1]求FWT(),然后每一位快速幂,在UFWT()回来即可。

【代码如下】

#include<cstdio>
#include<cstring>
using namespace std;
const int MOD=1e9+7;
int n,m,F[1<<16],INV;
int qsm(int x,int b){
	int Mul=1;
	for(;b;b>>=1,x=1ll*x*x%MOD) if(b&1) Mul=1ll*Mul*x%MOD;
	return Mul;
}
void FWT(int Len){
	for(int Step=1;Step<Len;Step<<=1)
	for(int i=0;i<Len;i+=(Step<<1))
	for(int j=0;j<Step;j++){
		int x=F[i+j],y=F[i+j+Step];
		F[i+j]=(x+y)%MOD;F[i+j+Step]=(x-y+MOD)%MOD;
	}
}
void UFWT(int Len){
	for(int Step=1;Step<Len;Step<<=1)
	for(int i=0;i<Len;i+=(Step<<1))
	for(int j=0;j<Step;j++){
		int x=F[i+j],y=F[i+j+Step];
		F[i+j]=1ll*(x+y)*INV%MOD;F[i+j+Step]=(1ll*(x-y)*INV%MOD+MOD)%MOD;
	}
}
int main(){
	INV=qsm(2,MOD-2);
	for(int Len;~scanf("%d%d",&n,&m);){
		memset(F,0,sizeof(F));F[0]=F[1]=1;
		for(int i=1;i<=m;i++) if(!F[i])
		for(int j=(i<<1);j<=m;j+=i) F[j]=1;
		for(int i=0;i<=m;i++) F[i]^=1;
		for(Len=1;Len<=m;Len<<=1);
		FWT(Len);
		for(int i=0;i<Len;i++) F[i]=qsm(F[i],n);
		UFWT(Len);
		printf("%d\n",F[0]);
	}
	return 0;
}
posted @ 2019-02-27 20:15  XSamsara  阅读(95)  评论(0编辑  收藏