BZOJ 3992 [SDOI2015]序列统计

题解:求m的原根,把乘法转化成加法,然后用NTT加速动态规划
听说这是循环卷积???并不会啊,留个坑。

NTT连板子都不熟

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1000009;
typedef long long Lint;
const Lint mm2=1004535809;

int n,m,fin,mm;

Lint g;
int ref[maxn];
void Getg(){
	for(g=2;g<mm;++g){
		Lint tmp=1;
		int fla=1;
		for(int i=1;i<mm-1;++i){
			tmp=tmp*g%mm;
			if(tmp==1){
				fla=0;break;
			}
		}
		if(fla)break;
	}
	Lint tmp=1;ref[1]=0;
	for(int i=1;i<mm-1;++i){
		tmp=tmp*g%mm;
		ref[tmp]=i;
	}
}

Lint Ksm(Lint a,Lint p){
	Lint ret=1;
	for(;p;p>>=1,a=a*a%mm2){
		if(p&1)ret=ret*a%mm2;
	}
	return ret;
}

Lint a[maxn];
Lint b[maxn];
Lint ret[maxn];

int l,rev[maxn];

void NTT(Lint *a,int f,int n){
	for(int i=0;i<n;++i)if(i<rev[i])swap(a[i],a[rev[i]]);
	
	for(int i=1;i<n;i<<=1){
		int p=i+i;
		Lint wn=Ksm(3,mm2/p);
		if(f==-1)wn=Ksm(wn,mm2-2);
		for(int j=0;j<n;j+=p){
			Lint w=1;
			for(int k=0;k<i;++k,w=w*wn%mm2){
				Lint x=a[j+k],y=a[j+k+i]*w%mm2;
				a[j+k]=(x+y)%mm2;a[j+k+i]=(x-y+mm2)%mm2;
			}
		}
	}
	if(f==-1){
		Lint inv=Ksm(n,mm2-2);
		for(int i=0;i<n;++i)a[i]=a[i]*inv%mm2;
	}
}

void Getans(int p){
	m=mm+mm-4;
	int n;
	for(n=1;n<=m;n<<=1)l++;
	for(int i=0;i<n;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
	ret[0]=1;
	for(;p;p>>=1){
		if(p&1){
			for(int i=0;i<n;++i)b[i]=a[i];
			NTT(ret,1,n);NTT(b,1,n);
			for(int i=0;i<n;++i)ret[i]=ret[i]*b[i]%mm2;
			NTT(ret,-1,n);
			for(int i=mm-1;i<n;++i){
				ret[i%(mm-1)]+=ret[i];ret[i]=0;
			}
			for(int i=0;i<mm-1;++i)ret[i]%=mm2;
		}
		for(int i=0;i<n;++i)b[i]=a[i];
		NTT(a,1,n);NTT(b,1,n);
		for(int i=0;i<n;++i)a[i]=a[i]*b[i]%mm2;
		NTT(a,-1,n);
		for(int i=mm-1;i<n;++i){
			a[i%(mm-1)]+=a[i];a[i]=0;
		}
		for(int i=0;i<mm-1;++i)a[i]%=mm2;
//		for(int i=0;i<n;++i)cout<<a[i]<<' ';
//		cout<<endl;
	}
}

int main(){
	scanf("%d%d%d%d",&n,&mm,&fin,&m);
	Getg();
	while(m--){
		int x;scanf("%d",&x);
		if(x!=0)a[ref[x]]++;
	}
	Getans(n);
	printf("%lld\n",ret[ref[fin]]%mm2);
	return 0;
}

  

posted @ 2018-02-21 14:41  ws_zzy  阅读(131)  评论(0编辑  收藏  举报