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
",b[num[sum]]);
return 0;
}
By NeighThorn