[UOJ 50][UR #3]链式反应
给你一个集合\(S\)(保证其中元素都为小于\(n\)的自然数),定义一棵合法的树为一棵编号满足堆的性质,且非叶子节点都一定有两个可能非叶子的儿子,同时有\(c(c\in S)\)个一定为叶子的儿子。对于所有\(i = 1\ldots n\),求有多少大小为\(i\)的形态不同的合法的树。
\(n\leq 200000\)。
毒瘤,毒瘤.jpg
先考虑DP,定义状态\(f(i)\)表示有\(i\)个点的合法树的数量,那么有:
\[
\begin{aligned}
f(n) &= \frac{1}{2}\sum_{j}\sum_{k}[(n - 1 - j - k)\in S]\binom{n - 1}{j}\binom{n - 1 - j}{k}f(j)f(k)\\
&= \frac{1}{2}\sum_{j}\sum_{k}[(n - 1 - j - k)\in S]\frac{(n - 1)!}{(n - 1 - j - k)!}\frac{f(j)}{j!}\frac{f(k)}{k!}
\end{aligned}
\]
那么定义\(g(n) = \sum_{i + j = n} \frac{f(i)f(j)}{i!j!}\),那么我们可以直接枚举\(j + k\),所以转移方程变成了:
\[
\begin{aligned}
f(n) &= \frac{1}{2}\sum_{s}[(n - 1 - s)\in S]\frac{(n - 1)!}{(n - 1 - s)!}g(s)
\end{aligned}
\]
然后就能\(O(n^2)\)做啦!虽然会T飞……
然后这里面出来了阶乘和组合数,考虑上指数型生成函数(下称EGF)!定义\(F(x)\)为\(f(i)\)的EGF,那么\(g(i)\)的EGF就是\(F^2(x)\),我们再定义\(C(x) = \sum_{i = 0}^{+\infty} [i\in S]\frac{1}{i!}\),然后我们得到了这样一个关系:
\[\frac{1}{2}C(x)F^2(x) + 1 = \frac{\mathrm{d}F(x)}{\mathrm{d}x}\]
然后你会发现这个微分方程搞不动了……(就是怎么查解都查不出来)
那么上多项式牛迭?我们还是考虑从膜低次的情况(假设为\(F_0(x)\))推到高次情况(假设为\(F(x)\),此时模式为\(x^p\)),顺便设\(h(t) = \frac{1}{2}C(x)t + 1\),那么很显然有\(\frac{\mathrm{d}F(x)}{\mathrm{d}x}\equiv h(F(x))\pmod{x^p}\),那这怎么搞?
我们还是使用传统艺能,将\(h\)在\(F_0(x)\)处泰勒展开,那么得到:
\[
\begin{aligned}
h(F(x))&\equiv h(F_0(x)) + \frac{h'(F_0(x))}{1!}(F(x) - F_0(x))^1 +\ldots\pmod{x^p}\\
&\equiv h(F_0(x)) + h'(F_0(x))F(x) - h'(F_0(x))F_0(x)\pmod{x^p}\\
\frac{\mathrm{d}F(x)}{\mathrm{d}x}&\equiv h(F_0(x)) + h'(F_0(x))F(x) - h'(F_0(x))F_0(x)\pmod{x^p}
\end{aligned}
\]
然后这泥萌还是没法做啊……
我们再挣扎一下,设\(v(x) = e^{-\int h'(F_0(x))\mathrm{d}x}\),那么我们将上面的等式两边乘\(v(x)\),得:
\[
\begin{aligned}
\frac{\mathrm{d}F(x)}{\mathrm{d}x}v(x)&\equiv v(x)(h(F_0(x)) + h'(F_0(x))F(x) - h'(F_0(x))F_0(x))\pmod{x^p}\\
\frac{\mathrm{d}F(x)}{\mathrm{d}x}v(x)-h'(F_0(x))F(x)v(x)&\equiv v(x)(h(F_0(x)) - h'(F_0(x))F_0(x))\pmod{x^p}\\
\frac{\mathrm{d}F(x)}{\mathrm{d}x}v(x)+F(x)\frac{\mathrm{d}v(x)}{\mathrm{d}x}&\equiv v(x)(h(F_0(x)) - h'(F_0(x))F_0(x))\pmod{x^p}\\
\frac{\mathrm{d}(F(x)v(x))}{\mathrm{d}x}&\equiv v(x)(h(F_0(x)) - h'(F_0(x))F_0(x))\pmod{x^p}\\
F(x)v(x)&\equiv \int v(x)(h(F_0(x)) - h'(F_0(x))F_0(x))\mathrm{d}x\pmod{x^p}\\
F(x)&\equiv \frac{1}{v(x)}\int v(x)(h(F_0(x)) - h'(F_0(x))F_0(x))\mathrm{d}x\pmod{x^p}
\end{aligned}
\]
然后这TM就可以倍增了……至于多项式exp、积分、求逆都是传统艺能了……顺便注意下常数……
#include <cstdio> #include <cstring> #include <cstdlib> #include <cassert> #include <algorithm> #include <functional> #include <utility> using ll = long long; constexpr ll ha = 998244353LL; inline ll pow_mod(ll a, ll b) { ll ans = 1, res = a; while(b) { if(1LL & b) ans = ans * res % ha; res = res * res % ha; b >>= 1; } return ans; } inline ll inv(ll x) { return pow_mod(x, ha - 2LL); } const int maxn = 524300; ll fac[maxn], ifac[maxn], invp[maxn]; inline void process() { invp[1] = 1; for(int i = 2; i < maxn; i ++) { invp[i] = (ha - (ha / (ll)i)); invp[i] = invp[i] * invp[ha % (ll)i] % ha; } fac[0] = 1; for(int i = 1; i < maxn; i ++) { fac[i] = fac[i - 1] * (ll)i % ha; } ifac[maxn - 1] = inv(fac[maxn - 1]); for(int i = maxn - 2; i >= 0; i --) { ifac[i] = (ll(i + 1)) * ifac[i + 1] % ha; } } inline void NTT(ll *A, int bi, bool flag = false) { int n = 1 << bi; for(int i = 1, j = 0; i < n; i ++) { int k = n; do j ^= (k >>= 1); while ((j & k) == 0); if(i < j) std::swap(A[j], A[i]); } for(int L = 1; L < n; L <<= 1) { const bool bb = (L < 4); ll c = (ha - 1LL) / (ll(L << 1)); if(flag) c = ha - 1LL - c; ll xi_n = pow_mod(3LL, c); ll *B = A + L; for(int i = 0; i < n; i += (L << 1)) { ll xi = 1; if(true) { for(int j = i; j < i + L; j ++) { ll x = A[j], y = B[j] * xi % ha; A[j] = (x + y) % ha; B[j] = (x - y + ha) % ha; xi = xi * xi_n % ha; } } else { for(int j = i; j < i + L; j += 4) { ll x = A[j], y = B[j] * xi % ha; A[j] = (x + y); if(A[j] >= ha) A[j] -= ha; B[j] = (x - y + ha); if(B[j] >= ha) B[j] -= ha; xi = xi * xi_n % ha; x = A[j + 1], y = B[j + 1] * xi % ha; A[j + 1] = (x + y); if(A[j + 1] >= ha) A[j + 1] -= ha; B[j + 1] = (x - y + ha); if(B[j + 1] >= ha) B[j + 1] -= ha; xi = xi * xi_n % ha; x = A[j + 2], y = B[j + 2] * xi % ha; A[j + 2] = (x + y); if(A[j + 2] >= ha) A[j + 2] -= ha; B[j + 2] = (x - y + ha); if(B[j + 2] >= ha) B[j + 2] -= ha; xi = xi * xi_n % ha; x = A[j + 3], y = B[j + 3] * xi % ha; A[j + 3] = (x + y); if(A[j + 3] >= ha) A[j + 3] -= ha; B[j + 3] = (x - y + ha); if(B[j + 3] >= ha) B[j + 3] -= ha; xi = xi * xi_n % ha; } } } } if(flag) { ll inv_n = invp[n]; for(int i = 0; i < n; i ++) { A[i] = A[i] * inv_n % ha; } } } void poly_inv(int mod, ll *B, ll *BB) { if(mod == 1) { BB[0] = inv(B[0]); } else { poly_inv((mod + 1) >> 1, B, BB); int bi = 0, sz = 1; while(sz <= ((mod * 2) - 1)) { bi ++; sz <<= 1; } static ll tmp[maxn]; std::copy(B, B + mod, tmp); std::fill(tmp + mod, tmp + sz, 0LL); NTT(tmp, bi); NTT(BB, bi); for(int i = 0; i < sz; i ++) { tmp[i] = (tmp[i] * BB[i]) % ha; tmp[i] = (tmp[i] * (ha - 1LL)) % ha; tmp[i] = (tmp[i] + 2LL) % ha; tmp[i] = (tmp[i] * BB[i]) % ha; } NTT(tmp, bi, true); std::copy(tmp, tmp + mod, BB); std::fill(BB + mod, BB + sz, 0LL); } } inline void poly_ln(int mod, ll *B, ll *BB) { int bi = 0, sz = 1; while(sz <= ((mod * 2) - 1)) { bi ++; sz <<= 1; } static ll tmp[maxn]; memset(tmp, 0, sizeof(ll) * sz); poly_inv(mod, B, BB); for(int i = 0; i < mod - 1; i ++) tmp[i] = B[i + 1] * (ll(i + 1)) % ha; NTT(tmp, bi); NTT(BB, bi); for(int i = 0; i < sz; i ++) BB[i] = BB[i] * tmp[i] % ha; NTT(BB, bi, true); for(int i = mod - 1; i > 0; i --) BB[i] = BB[i - 1] * invp[i] % ha; BB[0] = 0; std::fill(BB + mod, BB + sz, 0LL); // free(tmp); } void poly_exp(int mod, ll *B, ll *BB) { if(mod == 1) { BB[0] = 1; } else { poly_exp((mod + 1) >> 1, B, BB); int bi = 0, sz = 1; while(sz <= ((mod * 2) - 1)) { bi ++; sz <<= 1; } std::fill(BB + mod, BB + sz, 0LL); static ll tmp[maxn]; memset(tmp, 0, sizeof(ll) * sz); poly_ln(mod, BB, tmp); for(int i = 0; i < mod; i ++) { tmp[i] = (B[i] - tmp[i] + ha); if(tmp[i] >= ha) tmp[i] -= ha; } tmp[0] ++; NTT(tmp, bi); NTT(BB, bi); for(int i = 0; i < sz; i ++) BB[i] = tmp[i] * BB[i] % ha; NTT(BB, bi, true); std::fill(BB + mod, BB + sz, 0LL); // free(tmp); } } void poly_equation(int mod, ll *B, ll *BB) { if(mod == 1) { BB[0] = 0; } else { poly_equation((mod + 1) >> 1, B, BB); int bi = 0, sz = 1; while(sz <= (mod * 2 - 1)) { bi ++; sz <<= 1; } static ll t1[maxn], rv[maxn], v[maxn]; std::copy(B, B + mod, t1); memset(t1 + mod, 0, (sz - mod) * sizeof(ll)); NTT(t1, bi); // std::fill(BB + mod, BB + sz, 0LL); NTT(BB, bi); for(int i = 0; i < sz; i ++) rv[i] = (t1[i] * BB[i] % ha); NTT(rv, bi, true); for(int i = mod - 1; i > 0; i --) rv[i] = (ha - rv[i - 1] * invp[i] % ha); rv[0] = 0; poly_exp(mod, rv, v); static ll rn[maxn]; memset(rn, 0, sizeof(ll) * sz); // for(int i = 0; i < sz; i ++) rn[i] = BB[i] * BB[i] % ha; // NTT(rn, bi, true); std::fill(rn + mod, rn + sz, 0LL); // NTT(rn, bi); static const ll ii_2 = ha - inv(2); for(int i = 0; i < sz; i ++) { rn[i] = (1LL + (ii_2 * (BB[i] * BB[i] % ha) % ha) * t1[i]) % ha; } NTT(rn, bi, true); std::fill(rn + mod, rn + sz, 0LL); memset(rv, 0, sizeof(ll) * sz); poly_inv(mod, v, rv); NTT(v, bi); NTT(rn, bi); for(int i = 0; i < sz; i ++) rn[i] = rn[i] * v[i] % ha; NTT(rn, bi, true); std::fill(rn + mod, rn + sz, 0LL); for(int i = mod - 1; i > 0; i --) rn[i] = rn[i - 1] * invp[i] % ha; rn[0] = 0; NTT(rn, bi); NTT(rv, bi); for(int i = 0; i < sz; i ++) rn[i] = rn[i] * rv[i] % ha; NTT(rn, bi, true); std::copy(rn, rn + mod, BB); std::fill(BB + mod, BB + sz, 0LL); // free(rn); free(v); free(rv); free(t1); } } char S[maxn]; ll A[maxn], C[maxn]; int main() { #ifdef LOCAL freopen("gou.in", "r", stdin); freopen("outp", "w", stdout); srand(238238); for(int i = 0; i < 200000; i ++) { if(rand() & 1) S[i] = '1'; } #endif process(); int n; scanf("%d%s", &n, S); for(int i = 0; i < n; i ++) { if(S[i] == '1') { C[i] = ifac[i]; } } poly_equation(n + 1, C, A); for(int i = 1; i <= n; i ++) { printf("%lld\n", A[i] * fac[i] % ha); } return 0; }