\(\text{Description}\)
\(\text{Solution}\)
首先能想到朴素 \(\mathtt{dp}\):定义 \(f(i)\) 为元素 \(i\) 在序列中的组成方案数,那么相当于进行 \(n-1\) 次迭代:
\[f_n(i)=\sum_{jk=i}f_{n-1}(j)\cdot f(k)
\]
每次迭代之后将 \(>m\) 的部分叠到前面去。
事实上我们可以利用倍增来优化,就是把数与数列合并变成数列和数列合并,可以做到 \(\mathcal O(m^2\log n)\).
合并的形式其实与卷积十分相似,不妨考虑将乘法转换成加法。经典的转换方式就是取对数,如果在模意义下,相当于是 \(a^x\rightarrow x\). 另外还有一个重要的性质需要保证:\(a^x\) 与 \(x\) 是一一对应的关系。
所以不妨用 \(m\) 的原根(注意 \(m\) 是质数)来充作这个 \(a\). 不过此时可用的指数范围为 \([0,\varphi(m))\),只有 \(m-2\) 个数,但是显然 \(0\) 取不了对数(且 \(0\) 对答案也没有贡献),所以可以放心飞。需要注意的是此时下标范围为 \([0,\varphi(m))\),相当于对 \(\varphi(m)\) 取模。
用 \(\mathtt{NTT}\) 计算即可做到 \(\mathcal O(m\log m\log n)\).
\(\text{Code}\)
#include <cstdio>
#define print(x,y) write(x),putchar(y)
template <class T>
inline T read(const T sample) {
T x=0; char s; bool f=0;
while((s=getchar())>'9' || s<'0')
f |= (s=='-');
while(s>='0' && s<='9')
x = (x<<1)+(x<<3)+(s^48),
s = getchar();
return f?-x:x;
}
template <class T>
inline void write(T x) {
static int writ[50],w_tp=0;
if(x<0) putchar('-'),x=-x;
do writ[++w_tp]=x-x/10*10,x/=10; while(x);
while(putchar(writ[w_tp--]^48),w_tp);
}
#include <iostream>
using namespace std;
const int maxn = 8005;
const int mod = 1004535809;
const int ig = 334845270;
int n,m,rt,que,phi,inv,lim,bit;
int f[maxn<<2],g[maxn<<2],ref[maxn],rev[maxn<<2];
int qkpow(int x,int y,int mod) {
int r=1;
while(y) {
if(y&1) r=1ll*r*x%mod;
x=1ll*x*x%mod; y>>=1;
}
return r;
}
void getRoot() {
for(rt=2; ; ++rt) {
int x=phi; bool ban = false;
for(int i=2; i*i<=x; ++i) {
if(x%i) continue;
while(x%i==0) x/=i;
if(qkpow(rt,phi/i,m)==1) {
ban = true; break;
}
}
if(x>1 && qkpow(rt,phi/x,m)==1) ban=true;
if(!ban) return;
}
}
void preWork() {
lim=1;
while(lim<phi*2) lim<<=1, ++bit;
inv = qkpow(lim,mod-2,mod);
for(int i=0;i<lim;++i)
rev[i] = (rev[i>>1]>>1)|((i&1)<<bit-1);
}
inline void inc(int &x,int y) {
x = (x+y>=mod?x+y-mod:x+y);
}
inline int dec(int x,int y) {
return x-y<0?x-y+mod:x-y;
}
void NTT(int *f,bool opt=1) {
for(int i=0;i<lim;++i)
if(i<rev[i]) swap(f[i],f[rev[i]]);
int tmp,wn,w;
for(int mid=1;mid<lim;mid<<=1) {
wn = qkpow(opt?3:ig,(mod-1)/(mid<<1),mod);
for(int i=0;i<lim;i+=(mid<<1)) {
w=1;
for(int j=0;j<mid;++j,w=1ll*w*wn%mod) {
tmp = 1ll*f[i|j|mid]*w%mod;
f[i|j|mid] = dec(f[i|j],tmp);
inc(f[i|j],tmp);
}
}
}
if(!opt) for(int i=0;i<lim;++i) f[i]=1ll*f[i]*inv%mod;
}
void double_it() {
g[0]=1;
while(n) {
if(n&1) {
NTT(f), NTT(g);
for(int i=0;i<lim;++i)
g[i] = 1ll*f[i]*g[i]%mod;
NTT(f,0), NTT(g,0);
for(int i=phi;i<lim;++i)
inc(g[i%phi],g[i]), g[i]=0;
}
n>>=1; NTT(f);
for(int i=0;i<lim;++i)
f[i] = 1ll*f[i]*f[i]%mod;
NTT(f,0);
for(int i=phi;i<lim;++i)
inc(f[i%phi],f[i]), f[i]=0;
}
}
int main() {
n=read(9),m=read(9),que=read(9);
phi = m-1; getRoot();
for(int i=0;i<phi;++i)
ref[qkpow(rt,i,m)] = i;
for(int T=read(9); T; --T) {
int x=read(9);
if(x) ++ f[ref[x]];
}
preWork(); double_it();
print(g[ref[que]],'\n');
return 0;
}