Luogu 3321 [SDOI2015]序列统计
BZOJ 3992
点开这道题之后才发现我对原根的理解大概只停留在$998244353$的原根是$3$……
关于原根: 点我
首先写出$dp$方程,设$f_{i, j}$表示序列长度为$i$当前所有数乘积模$m$为$j$的方案数,有转移
$$f_{i, x * y \mod m} = \sum_{y \in s} f_{i - 1, x}$$
把$x$和$y$取个对数就可以变成卷积的形式了。
然而在模意义下,我们可以用原根的$k$次方来代替原来的数,这样子就达到了取对数的效果。
注意到每一次转移形式都是相同的,我们可以用快速幂的方式来优化。
时间复杂度$O(mlogmlogn)$。
然而我的代码只有在有$c++11$的时候才是对的,哪位大佬如果知道了为什么教教我呗。
Code:
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; typedef long long ll; typedef vector <ll> poly; const int N = 8005; int n, m, tar, fac[N], buc[N]; namespace Poly { const int L = 1 << 14; const ll P = 1004535809LL; int lim, pos[L]; template <typename T> inline void inc(T &x, T y) { x += y; if (x >= P) x -= P; } inline ll fpow(ll x, ll y, ll mod = P) { ll res = 1; for (; y > 0; y >>= 1) { if (y & 1) res = res * x % mod; x = x * x % mod; } return res; } inline void prework(int len) { int l = 0; for (lim = 1; lim < len; lim <<= 1, ++l); for (int i = 0; i < lim; i++) pos[i] = (pos[i >> 1] >> 1) | ((i & 1) << (l - 1)); } inline void ntt(poly &c, int opt) { c.resize(lim, 0); for (int i = 0; i < lim; i++) if (i < pos[i]) swap(c[i], c[pos[i]]); for (int i = 1; i < lim; i <<= 1) { ll wn = fpow(3, (P - 1) / (i << 1)); if (opt == -1) wn = fpow(wn, P - 2); for (int len = i << 1, j = 0; j < lim; j += len) { ll w = 1; for (int k = 0; k < i; k++, w = w * wn % P) { ll x = c[j + k], y = w * c[j + k + i] % P; c[j + k] = (x + y) % P, c[j + k + i] = (x - y + P) % P; } } } if (opt == -1) { ll inv = fpow(lim, P - 2); for (int i = 0; i < lim; i++) c[i] = c[i] * inv % P; } } poly operator * (const poly x, const poly y) { poly u = x, v = y, res; prework(x.size() + y.size() - 1); ntt(u, 1), ntt(v, 1); for (int i = 0; i < lim; i++) res.push_back(u[i] * v[i] % P); ntt(res, -1); res.resize(x.size() + y.size() - 1); return res; } inline void adj(poly &c) { for (int i = 0; i < m - 1; i++) inc(c[i], c[i + m - 1]); c.resize(m - 1); } inline poly pow(poly x, int y) { poly res; res.resize(m - 1); res[0] = 1; for (; y; y >>= 1) { if (y & 1) res = res * x, adj(res); x = x * x, adj(x); } return res; } } using Poly :: P; using Poly :: fpow; using Poly :: pow; template <typename T> inline void read(T &X) { X = 0; char ch = 0; T op = 1; for (; ch > '9'|| ch < '0'; ch = getchar()) if (ch == '-') op = -1; for (; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } inline int getRoot() { int cnt = 0; for (int i = 2; i <= m - 2; i++) if ((m - 1) % i == 0) fac[++cnt] = i; for (int i = 2; ; i++) { bool flag = 1; for (int j = 1; j <= cnt; j++) if (fpow(i, fac[j], m) == 1) { flag = 0; break; } if (flag) return i; } } int main() { /* #ifndef ONLINE_JUDGE freopen("8.in", "r", stdin); #endif */ int s, root; read(n), read(m), read(tar), read(s); root = getRoot(); for (int i = 1; i <= m - 2; i++) buc[fpow(root, i, m)] = i; poly trans; trans.resize(m - 1); for (int x, i = 1; i <= s; i++) { read(x); if (!x) continue; trans[buc[x]] = 1; } poly ans = pow(trans, n); printf("%lld\n", ans[buc[tar]]); return 0; }