BZOJ 3992: [SDOI2015]序列统计
3992: [SDOI2015]序列统计
Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 1147 Solved: 527
[Submit][Status][Discuss]
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中元素不重复
Source
分析:
首先考虑最直接的算法$DP$...$f[i][j]$代表前$i$位乘积为$j$的方案数,但是$n$是$1o^9$...感觉这个数据范围告诉我们“我叫快速幂”...
考虑原根$p$的定义,所以我们可以把每个数字用$p$的不同次幂表示,这就把乘法变成了加法...貌似可以用$NTT$来优化...
所以我们就得到了一个优秀的$O(MlogMlogN)$的算法...
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> //by NeighThorn using namespace std; const int maxn=20000+5,mod=1004535809,G=3; int n,k,m,N,L,t,sum,tim,root,s[maxn],R[maxn],a[maxn],b[maxn],vis[maxn],num[maxn]; inline int power(int x,int y){ int res=1; while(y){ if(y&1) res=1LL*res*x%mod; x=1LL*x*x%mod,y>>=1; } return res; } inline void NTT(int *a,int f){ for(int i=0;i<N;i++) if(i<R[i]) swap(a[i],a[R[i]]); for(int i=1;i<N;i<<=1){ int wn=power(G,(mod-1)/(i<<1)); if(f==-1) wn=power(wn,mod-2); for(int j=0;j<N;j+=(i<<1)){ int w=1; for(int k=0;k<i;k++,w=1LL*w*wn%mod){ int x=a[j+k],y=1LL*w*a[j+k+i]%mod; a[j+k]=(x+y)%mod; a[j+k+i]=((x-y)%mod+mod)%mod; } } } if(f==-1){ int tmp=power(N,mod-2); for(int i=0;i<N;i++) a[i]=1LL*a[i]*tmp%mod; } } inline bool check(int x){ int no=x;tim++; for(int i=1;i<n;i++,no=1LL*no*x%n){ if(vis[no]==tim) return false; vis[no]=tim; } return true; } inline int findroot(void){ for(int i=2;i<n;i++) if(check(i)) return i; } inline void power(int y){ memset(b,0,sizeof(b));b[0]=1; while(y){ NTT(a,1); if(y&1){ NTT(b,1); for(int i=0;i<N;i++) b[i]=1LL*a[i]*b[i]%mod; NTT(b,-1); for(int i=N-1;i>=n-1;i--) b[i-n+1]=(b[i-n+1]+b[i])%mod,b[i]=0; } for(int i=0;i<N;i++) a[i]=1LL*a[i]*a[i]%mod;NTT(a,-1); for(int i=N-1;i>=n-1;i--) a[i-n+1]=(a[i-n+1]+a[i])%mod,a[i]=0; y>>=1; } } signed main(void){ #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); #endif scanf("%d%d%d%d",&k,&n,&sum,&m); for(int i=1;i<=m;i++) scanf("%d",&s[i]); t=n<<1;for(N=1;N<=t;N<<=1) L++; for(int i=0;i<N;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(L-1)); root=findroot();int tmp=1;memset(a,0,sizeof(a)); for(int i=0;i<n-1;i++) num[tmp]=i,tmp=1LL*tmp*root%n; for(int i=1;i<=m;i++) if(s[i]) a[num[s[i]]]++;power(k); printf("%d\n",b[num[sum]]); return 0; }
By NeighThorn