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

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 }

 

posted @ 2017-04-05 22:30  wfj_2048  阅读(268)  评论(0编辑  收藏  举报