2018.12.31 bzoj3992: [SDOI2015]序列统计(生成函数+ntt+快速幂)

时间:2023-03-09 19:26:41
2018.12.31 bzoj3992: [SDOI2015]序列统计(生成函数+ntt+快速幂)

传送门

生成函数简单题。

题意:给出一个集合A={a1,a2,...as}A=\{a_1,a_2,...a_s\}A={a1​,a2​,...as​},所有数都在[0,m−1][0,m-1][0,m−1]之间,mmm是一个质数,求满足全部由这个集合里的组成且长度为nnn且所有数之积与xxx在模mmm意义下相同的数列总数。


思路:对a1,a2,..,as,xa_1,a_2,..,a_s,xa1​,a2​,..,as​,x全部化成gb1,gb2,...gbs,gyg^{b_1},g^{b_2},...g^{b_s},g^{y}gb1​,gb2​,...gbs​,gy,然后就转乘法为加法。

于是只用求x1+x2+...+xn≡ymod  m−1x_1+x_2+...+x_n\equiv y\mod m-1x1​+x2​+...+xn​≡ymodm−1,其中xi∈{b1,b2,...bs}x_i\in\{b_1,b_2,...b_s\}xi​∈{b1​,b2​,...bs​}的方案数。

对于xix_ixi​可以构造出生成函数1+xb1+xb2+...+xbs1+x^{b_1}+x^{b_2}+...+x^{b_s}1+xb1​+xb2​+...+xbs​,于是答案的生成函数就是(1+xb1+xb2+...+xbs)n(1+x^{b_1}+x^{b_2}+...+x^{b_s})^n(1+xb1​+xb2​+...+xbs​)n,考虑到nnn很大可以用快速幂+nttnttntt解决。

注意特判ai=0a_i=0ai​=0的情况以及每次nttnttntt完了之后要重新把多项式的最高次数控制为m−2m-2m−2

代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
typedef long long ll;
const int N=3e6+5,mod=1004535809;
int M,lim=1,tim=0,pos[N],a[N],P,n,x,idx[N];
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a>=b?a-b:a-b+mod;}
inline int mul(int a,int b){return (ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)ret=mul(ret,a);return ret;}
inline void ntt(int a[],int type){
	for(ri i=0;i<lim;++i)if(i<pos[i])swap(a[i],a[pos[i]]);
	for(ri wn,mult=(mod-1)>>1,typ=type==1?3:334845270,mid=1;mid<lim;mid<<=1,mult>>=1){
		wn=ksm(typ,mult);
		for(ri j=0,len=mid<<1;j<lim;j+=len)for(ri w=1,k=0,a0,a1;k<mid;++k,w=mul(w,wn)){
			a0=a[j+k],a1=mul(w,a[j+k+mid]);
			a[j+k]=add(a0,a1),a[j+k+mid]=dec(a0,a1);
		}
	}
	if(type==-1)for(ri inv=ksm(lim,mod-2),i=0;i<lim;++i)a[i]=mul(a[i],inv);
}
inline void poly_mul(int a[],int b[]){
	static int A[N],B[N];
	for(ri i=0;i<lim;++i)A[i]=a[i],B[i]=b[i];
	ntt(A,1),ntt(B,1);
	for(ri i=0;i<lim;++i)A[i]=mul(A[i],B[i]);
	ntt(A,-1);
	for(ri i=0;i<lim;++i)a[i]=A[i];
	for(ri i=lim-1;i>=M-1;--i)a[i-M+1]=add(a[i-M+1],a[i]),a[i]=0;
}
inline void solve(int a[],int p){
	static int ret[N];
	memset(ret,0,sizeof(ret)),ret[0]=1;
	for(;p;p>>=1,poly_mul(a,a))if(p&1)poly_mul(ret,a);
	cout<<ret[x];
}
inline bool check(int g){
	for(ri i=0;i<M;++i)idx[i]=-1;
	for(ri i=0,mul=1;i<M-1;++i,mul=mul*g%M){
		if(~idx[mul])return 0;
		idx[mul]=i;
	}
	return 1;
}
inline void init(){
	while(lim<=M*2)lim<<=1,++tim;
	for(ri i=0;i<lim;++i)pos[i]=(pos[i>>1]>>1)|((i&1)<<(tim-1));
	for(ri g=1;!check(g);++g);
}
int main(){
    P=read(),M=read(),init(),x=idx[read()],n=read();
    while(n--){
    	int x=read();
    	if(!x)continue;
    	a[idx[x]]=1;
	}
    solve(a,P);
    return 0;
}