3992: [SDOI2015]序列统计
【题目描述】
传送门
【题解】
我们可以写出DP式,F[i][j∗a[k]]+=F[i−1][j]F[i][j*a[k]]+=F[i-1][j]F[i][j∗a[k]]+=F[i−1][j]
初始状态F[0][0]=1F[0][0]=1F[0][0]=1
对于上式我们很难处理,如果我们可以将相乘改成相加,就可以套NTT了。
我们设ggg为mod m意义下的原根。
设j=gbjj=g^bjj=gbj,a[k]=gba[k]a[k]=g^{ba[k]}a[k]=gba[k]
上式就可以写成F[i][gbj+ba[k]]+=F[i−1][gbj]F[i][g^{bj+ba[k]}]+=F[i-1][g^{bj}]F[i][gbj+ba[k]]+=F[i−1][gbj]
也就是F[i][bj+ba[k]]+=F[i−1][bj]F[i][bj+ba[k]]+=F[i-1][bj]F[i][bj+ba[k]]+=F[i−1][bj]
这不就是卷积的形式了吗,直接套NTT就可以了
【代码如下】
#include
#include
using namespace std;
const int MAXN=8005,MOD=1004535809;
int Len,lg2,g,inv,pos,T,m,X,n,rev[MAXN<<2],Mu[MAXN<<2],a[MAXN<<2],F[MAXN<<2],G[MAXN<<2],NI;bool vis[MAXN];
int qsm(int x,int b,int p){
int Mul=1;
for(;b;b>>=1,x=1ll*x*x%p) if(b&1) Mul=1ll*Mul*x%p;
return Mul;
}
int Cal(){//求m的原根
if(m==2) return 1;
for(int i=2;;i++){
bool f=1;
for(int j=2;j*j>1]>>1)|((i&1)<<(lg2-1));}
void NTT(int *A,int opt){
for(int i=0;i>=1,Mul(a,a)) if(T&1) Mul(Mu,a);}
int main(){
scanf("%d%d%d%d",&T,&m,&X,&n);
for(int i=1,x;i<=n;i++) scanf("%d",&x),vis[x]=1;
g=Cal();
for(int i=0,x=1;i