Description
Solution
我是来复习NTT板子的。。
容易得到一个暴力DP方法,设
fi,j
f
i
,
j
表示到第
i
i
位,当前余数为的方案数。
将其像快速幂一样转移可以将
n
n
优化成。
考虑怎么继续优化:
我们发现
f[l][k]=∑i×j≡k(modm)f[l−1][j]×f[l−1][k]
f
[
l
]
[
k
]
=
∑
i
×
j
≡
k
(
mod
m
)
f
[
l
−
1
]
[
j
]
×
f
[
l
−
1
]
[
k
]
如果是
i+j
i
+
j
我们就可以用NTT来优化了,我们可以将
i×j
i
×
j
用原根转化一下:
由于
m
m
是质数,所以我们可以找到的原根
g0
g
0
,由原根的定义可以知道
g00,g10…gm−20modm
g
0
0
,
g
0
1
…
g
0
m
−
2
mod
m
不遗漏、不重复地组成了
1…m−1
1
…
m
−
1
这些数,我们用
g0
g
0
的次幂表示给定的集合内的数,就可以将乘法转化成加法,然后NTT即可。
Code
/**************************************
* Au: Hany01
* Prob: [BZOJ3992][SDOI2015] 序列统计
* Date: Jul 26th, 2018
* Email: hany01@foxmail.com
**************************************/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef vector<int> VI;
#define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout)
#define rep(i, j) for (register int i = 0, i##_end_ = j; i < i##_end_; ++ i)
#define For(i, j ,k) for (register int i = (j), i##_end_ = (k); i <= i##_end_; ++ i)
#define Fordown(i, j, k) for (register int i = (j), i##_end_ = (k); i >= i##_end_; -- i)
#define Set(a, b) memset(a, b, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define SZ(a) ((int)(a.size()))
#define ALL(a) a.begin(), a.end()
#define pb(a) push_back(a)
#define mp(a, b) make_pair(a, b)
#define x first
#define y second
#define INF (0x3f3f3f3f)
#define INF1 (2139062143)
#define MOD (1004535809)
#define g (3)
#define y1 wozenmezhemecaia
#ifdef hany01
#define debug(...) fprintf(stderr, __VA_ARGS__)
#else
#define debug(...)
#endif
template<typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }
template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
inline int read() {
register char c_; register int _, __;
for (_ = 0, __ = 1, c_ = getchar(); !isdigit(c_); c_ = getchar()) if (c_ == '-') __ = -1;
for ( ; isdigit(c_); c_ = getchar()) _ = (_ << 1) + (_ << 3) + (c_ ^ 48);
return _ * __;
}
const int maxn = 8005;
int n, M, N, rev[maxn << 2], re[maxn];
LL I[maxn << 2], A[maxn << 2], powg[maxn << 2], ipowg[maxn << 2], invn, g0, ig;
inline LL Pow(LL a, LL b, LL mod = MOD) {
static LL Ans;
for (Ans = 1; b; b >>= 1, (a *= a) %= mod) if (b & 1) (Ans *= a) %= mod;
return Ans;
}
inline int getg0(int x)
{
static int t = x - 1, tot, pr[60], mk;
for (register int i = 2; i * i <= t; ++ i) if (!(t % i)) {
pr[++ tot] = i;
do t /= i; while (!(t % i));
}
if (t > 1) pr[++ tot] = t;
for (register int g0 = 2; ; ++ g0) {
mk = 1;
For(i, 1, tot) if (Pow(g0, (x - 1) / pr[i], x) == 1) { mk = 0; break; }
if (mk) return g0;
}
}
inline int ad(int x, int y) { if ((x += y) >= MOD) return x - MOD; return x; }
inline void NTT(LL* a, int ty) {
rep(i, n) if (i < rev[i]) swap(a[i], a[rev[i]]);
for (register int i = 2, p = 1; i <= n; p = i, i <<= 1) {
register LL w0 = ty ? powg[i] : ipowg[i];
for (register int j = 0; j < n; j += i) {
register LL w = 1;
rep(k, p) {
register LL x = a[j + k], y = a[j + p + k] * w % MOD;
a[j + k] = ad(x, y), a[j + p + k] = ad(x, MOD - y);
(w *= w0) %= MOD;
}
}
}
if (!ty) rep(i, n) (a[i] *= invn) %= MOD;
}
inline void mult(LL* a, LL* b, LL* c) {
static LL A[maxn << 2], B[maxn << 2];
Set(A, 0), Set(B, 0);
rep(i, M - 1) A[i] = a[i], B[i] = b[i];
NTT(A, 1), NTT(B, 1);
rep(i, n) (A[i] *= B[i]) %= MOD;
NTT(A, 0);
rep(i, M - 1) c[i] = ad(A[i], A[i + M - 1]);
}
int main()
{
#ifdef hany01
File("bzoj3992");
#endif
static LL t;
static int anspos, hst, cnt, S, tmp;
S = read(), M = read(), anspos = read(), N = read(), t = 1, g0 = getg0(M);
rep(i, M - 1) re[t] = i, (t *= g0) %= M;
For(i, 1, N) if (tmp = read()) I[re[tmp]] = 1;
for (A[0] = 1, hst = (M - 2) << 1, cnt = 0, n = 1; n <= hst; n <<= 1, ++ cnt);
rep(i, n) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (cnt - 1));
ig = Pow(g, MOD - 2), invn = Pow(n, MOD - 2);
for (register int i = 1; i <= n; i <<= 1) powg[i] = Pow(g, (MOD - 1) / i), ipowg[i] = Pow(ig, (MOD - 1) / i);
for ( ; S; S >>= 1, mult(I, I, I)) if (S & 1) mult(A, I, A);
cout << A[re[anspos]] << endl;
return 0;
}