题目描述:
雾.
题目分析:
首先写出朴素的DP方程
DP[i][j]为选了i个数当前乘积为j的方案数
D
P
[
i
]
[
j
]
为
选
了
i
个
数
当
前
乘
积
为
j
的
方
案
数
DP[i+1][(j∗k)%m]=∑kDP[i][j]∗C[k]
D
P
[
i
+
1
]
[
(
j
∗
k
)
%
m
]
=
∑
k
D
P
[
i
]
[
j
]
∗
C
[
k
]
C[S]为集合S是否存在K
这个DP为
O(m2∗n)
O
(
m
2
∗
n
)
看到 m 的特殊性 为一个质数
引入一个原根的概念
对于一个素数p,他的一个原根g的0~p-2次幂在模p意义下取遍1~p-1的所有值
求原根可以先找出p-1的所有质因数,然后从2开始枚举,如果对任意的p-1的质因数p[i],x的(p-1)/p[i]次幂模p都不等于1,则x是p的一个原根
原根一般比较小,可以暴力找
那么上面的DP方程就可以写成
DP[i+1][(j+k)%(M−1)]=∑kDP[i][j]∗C[k]
D
P
[
i
+
1
]
[
(
j
+
k
)
%
(
M
−
1
)
]
=
∑
k
D
P
[
i
]
[
j
]
∗
C
[
k
]
很明显就是一个卷积的形式了
套用多项式快速幂即可得到答案
最后复杂度为
O(logn∗m∗logm)
O
(
l
o
g
n
∗
m
∗
l
o
g
m
)
另外本题的模数是符合NTT形式的.
题目链接:
BZOJ 3992
COGS 1981
Luogu 3321
Ac 代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define int long long
const int mod=1004535809,g=3,gi=334845270;
const int maxm=8000*100;
inline int fastpow(int x,int y,int p=mod)
{
int ans=1;
for(;y;y>>=1,x=(x*x)%p)
if(y&1) ans=(ans*x)%p;
return ans%p;
}
int rev[maxm],bin[maxm];
inline void NTT(int *a,int n,int f)
{
int len=bin[n];
for(int i=0;i<n;i++) rev[i]=((rev[i>>1]>>1)|((i&1)<<(len-1)));
for(int i=0;i<n;i++) if(i<rev[i]) std::swap(a[i],a[rev[i]]);
for(int i=1;i<n;i<<=1)
{
int wn=fastpow((f==1)?g:gi,(mod-1)/(i<<1));
for(int j=0;j<n;j+=(i<<1))
{
int w=1;
for(int k=0;k<i;k++,w=(wn*w)%mod)
{
int x=a[j+k],y=(w*a[i+j+k])%mod;
a[j+k]=(x+y)%mod,a[i+j+k]=(x-y+mod)%mod;
}
}
}
if(f==-1)
{
int inv=fastpow(n,mod-2);
for(int i=0;i<n;i++) a[i]=(a[i]*inv)%mod;
}
}
int n,m,x,s,G,H[maxm],C[maxm],vis[maxm];//H为数对应的幂次,C为存在性
int ans[maxm];
inline void init()
{
for(int i=1;i<m;i++)
{
memset(vis,0,sizeof(vis));
int flag=1;
for(int j=1;j<m&&flag;j++)
{
int tmp=fastpow(i,j,m);
if(vis[tmp]) flag=0;
vis[tmp]=1;
}
if(flag)
{
G=i;
break;
}
}
//printf("%lld\n",G);
for(int i=0;i<m;i++) H[fastpow(G,i,m)]=i;
for(int i=0;i<18;i++) bin[1<<i]=i;
}
inline void getans(int *a,int n,int y)
{
y--;
for(int i=0;i<n;i++) ans[i]=a[i];
while(y)
{
if(y&1)
{
NTT(ans,n,1),NTT(a,n,1);
for(int i=0;i<n;i++) ans[i]=(ans[i]*a[i])%mod;
NTT(a,n,-1),NTT(ans,n,-1);
for(int i=m-1;i<n;i++) ans[i%(m-1)]=(ans[i%(m-1)]+ans[i])%mod,ans[i]=0;
}
NTT(a,n,1);
for(int i=0;i<n;i++) a[i]=(a[i]*a[i])%mod;
NTT(a,n,-1);
for(int i=m-1;i<n;i++) a[i%(m-1)]=(a[i%(m-1)]+a[i])%mod,a[i]=0;
y/=2;
}
}
signed main()
{
//freopen("sdoi2015_sequence.in","r",stdin);
//freopen("sdoi2015_sequence.out","w",stdout);
scanf("%lld%lld%lld%lld",&n,&m,&x,&s);
init();
for(int i=1;i<=s;i++)
{
int num;
scanf("%lld",&num);
if(num) C[H[num]%(m-1)]=1;
}
int Len;
for(Len=1;Len<=2*m;Len<<=1);
getans(C,Len,n);
printf("%lld\n",ans[H[x]%(m-1)]);
return 0;
}