bzoj3992 [SDOI2015]序列统计
Description
小C有一个集合S,里面的元素都是小于M的非负整数。他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S。
小C用这个生成器生成了许多这样的数列。但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中所有数的乘积mod M的值等于x的不同的数列的有多少个。小C认为,两个数列{Ai}和{Bi}不同,当且仅当至少存在一个整数i,满足Ai≠Bi。另外,小C认为这个问题 的答案可能很大,因此他只需要你帮助他求出答案mod 1004535809的值就可以了。
Input
一行,四个整数,N、M、x、|S|,其中|S|为集合S中元素个数。第二行,|S|个整数,表示集合S中的所有元素。
Output
一行,一个整数,表示你求出的种类数mod 1004535809的值。
Sample Input
4 3 1 2
1 2
1 2
Sample Output
8
HINT
【样例说明】
可以生成的满足要求的不同的数列有(1,1,1,1)、(1,1,2,2)、(1,2,1,2)、(1,2,2,1)、(2,1,1,2)、(2,1,2,1)、(2,2,1,1)、(2,2,2,2)。
【数据规模和约定】
对于10%的数据,1<=N<=1000;
对于30%的数据,3<=M<=100;
对于60%的数据,3<=M<=800;
对于全部的数据,1<=N<=109,3<=M<=8000,M为质数,1<=x<=M-1,输入数据保证集合S中元素不重复
正解:$DP$+$NTT$。
首先考虑暴力$DP$,$f[i][j]$表示统计到第$i$位,乘积为$j$的方案数,然后很显然,$f[i][(k*p)\mod m]=f[i-1][k]*sum[p]$,其中$sum[p]$为p这个数出现的次数。
但是这样是肯定会$T$飞的,所以我们考虑优化。话说这个优化还是蛮玄学的。。
我们可以把乘法变成加法,然后两个数相乘就是它们的对数相加,当然底数要相同,并且对数肯定要是整数。
因为$m$是质数,所以$m$必定有原根。我们又知道,原根的$[1,m-1]$次方对应了$[1,m-1]$这些数,所以我们可以求出$[1,m-1]$的离散对数,也就是使得原根$g^{x}=i$的$x$(然而我没学过所以不会。。)
设$ind[i]$为$i$在模$m$意义下的离散对数,于是$f[i][(ind[k]+ind[p])\mod (m-1)]=f[i-1][ind[k]]*sum[ind[p]]$(注意这里第二维的取值范围是$m-1$,所以还要加一个特判)
因为答案对$1004535809$取模,所以显然这个方程可以用$NTT$优化,但是$n$的范围很大,有$10^{9}$级别,不过我们在卷积外面套一个快速幂就行了。
我们先用快速幂算出$sum$的$n$次方,然后再用$f[0]*sum$,最后输出$f[n][ind[x]]$,就是我们所要的答案。
1 //It is made by wfj_2048~ 2 #include <algorithm> 3 #include <iostream> 4 #include <complex> 5 #include <cstring> 6 #include <cstdlib> 7 #include <cstdio> 8 #include <vector> 9 #include <cmath> 10 #include <queue> 11 #include <stack> 12 #include <map> 13 #include <set> 14 #define rhl (1004535809) 15 #define inf (1<<30) 16 #define NN (100010) 17 #define G (3) 18 #define il inline 19 #define RG register 20 #define ll long long 21 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) 22 23 using namespace std; 24 25 int rev[NN],ind[NN],N,n,m,x,s,lg,gg; 26 ll ans[NN],a[NN],b[NN],sum[NN]; 27 28 il int gi(){ 29 RG int x=0,q=1; RG char ch=getchar(); 30 while ((ch<'0' || ch>'9') && ch!='-') ch=getchar(); 31 if (ch=='-') q=-1,ch=getchar(); 32 while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); 33 return q*x; 34 } 35 36 il ll qpow(RG ll a,RG ll b){ 37 RG ll ans=1; 38 while (b){ 39 if (b&1) (ans*=a)%=rhl; 40 (a*=a)%=rhl,b>>=1; 41 } 42 return ans; 43 } 44 45 il void NTT(ll *a,RG int n,RG int f){ 46 for (RG int i=0;i<n;++i) if (i<rev[i]) swap(a[i],a[rev[i]]); 47 for (RG int i=1;i<n;i<<=1){ 48 RG ll gn=qpow(G,(rhl-1)/(i<<1)),x,y; 49 for (RG int j=0;j<n;j+=(i<<1)){ 50 RG ll g=1; 51 for (RG int k=0;k<i;++k,(g*=gn)%=rhl){ 52 x=a[j+k],y=g*a[j+k+i]%rhl; 53 a[j+k]=(x+y)%rhl,a[j+k+i]=(x-y+rhl)%rhl; 54 } 55 } 56 } 57 if (f==1) return; reverse(a+1,a+n); RG ll inv=qpow(n,rhl-2); 58 for (RG int i=0;i<n;++i) (a[i]*=inv)%=rhl; 59 for (RG int i=m;i<n;++i) (a[i%(m-1) ? i%(m-1) : m-1]+=a[i])%=rhl,a[i]=0; return; 60 } 61 62 il void NTTpow(ll *a,RG int b){ 63 for (RG int i=0;i<N;++i) ans[i]=a[i]; b--; 64 while (b){ 65 NTT(a,N,1); 66 if (b&1){ 67 NTT(ans,N,1); 68 for (RG int i=0;i<N;++i) (ans[i]*=a[i])%=rhl; 69 NTT(ans,N,-1); 70 } 71 for (RG int i=0;i<N;++i) (a[i]*=a[i])%=rhl; 72 NTT(a,N,-1); b>>=1; 73 } 74 for (RG int i=0;i<N;++i) a[i]=ans[i]; return; 75 } 76 77 il int isroot(RG int x){ 78 RG int l=1; 79 for (RG int i=1;i<m-1;++i){ 80 (l*=x)%=m; 81 if (l==1) return 0; 82 } 83 return 1; 84 } 85 86 il void pre(){ 87 for (RG int i=2;i<m;++i) if (isroot(i)){ gg=i; break; } 88 RG int l=1; for (RG int i=1;i<m;++i) (l*=gg)%=m,ind[l]=i; return; 89 } 90 91 il void work(){ 92 n=gi(),m=gi(),x=gi(),s=gi(); pre(); 93 for (RG int i=1,v;i<=s;++i) sum[ind[v=gi()]]++; 94 for (N=1;N<=(m<<1);N<<=1) lg++; a[ind[1]]=1,sum[0]=0; 95 for (RG int i=0;i<N;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1)); 96 NTTpow(sum,n); NTT(a,N,1),NTT(sum,N,1); 97 for (RG int i=0;i<N;++i) (a[i]*=sum[i])%=rhl; 98 NTT(a,N,-1); printf("%lld",a[ind[x]]); return; 99 } 100 101 int main(){ 102 File("sequence"); 103 work(); 104 return 0; 105 }