luoguP4492 [HAOI2018]蘋果樹 組合計數 + dp
阿新 • • 發佈:2018-12-25
首先,每個二叉樹對應著唯一的中序遍歷,並且每個二叉樹的概率是相同的
這十分的有用
考慮\(dp\)求解
令\(f_i\)表示\(i\)個節點的子樹,根的深度為\(1\)時,所有點的期望深度之和(乘\(i!\))的值
令\(g_i\)表示\(i\)個節點的子樹,期望兩兩路徑之和(乘\(i!\))的值
那麼\(f_i = i * i! + \sum \limits_{L = 0}^{i - 1} \binom{i - 1}{L} (f_L * R! + f_R * L!)\),\(L, R\)分別表示左右子樹的值
\(g_i = \sum \limits_{L = 0}^{i - 1} \binom{i - 1}{L} (g_L * R! + g_R * L! + f_L * R! * (R + 1) + f_R * L! * (L + 1))\)
複雜度\(O(n^2)\)
由於沒有逆元,因此組合數要預處理
好像可以用多項式優化到\(O(n \log^2 n)\)QAQ
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define ri register int #define rep(io, st, ed) for(ri io = st; io <= ed; io ++) #define drep(io, ed, st) for(ri io = ed; io >= st; io --) const int sid = 2e3 + 5; int n, mod; int C[sid][sid], fac[sid], f[sid], g[sid]; inline void inc(int &a, int b) { a += b; if(a >= mod) a -= mod; } inline void dec(int &a, int b) { a -= b; if(a < 0) a += mod; } inline int Inc(int a, int b) { return (a + b >= mod) ? a + b - mod : a + b; } inline int Dec(int a, int b) { return (a - b < 0) ? a - b + mod : a - b; } inline int mul(int a, int b) { return 1ll * a * b % mod; } inline void init() { fac[0] = C[0][0] = 1; rep(i, 1, n) { C[i][0] = C[i][i] = 1; fac[i] = mul(fac[i - 1], i); rep(j, 1, i - 1) C[i][j] = Inc(C[i - 1][j], C[i - 1][j - 1]); } } inline void dp() { f[1] = 1; rep(i, 2, n) { rep(L, 0, i - 1) { int R = i - 1 - L, F = 0, G = 0; F = (1ll * f[L] * fac[R] + 1ll * f[R] * fac[L]) % mod; G = (1ll * f[L] * fac[R] % mod * (R + 1) % mod); inc(G, 1ll * f[R] * fac[L] % mod * (L + 1) % mod); inc(G, 1ll * g[L] * fac[R] % mod); inc(G, 1ll * g[R] * fac[L] % mod); inc(f[i], mul(F, C[i - 1][L])); inc(g[i], mul(G, C[i - 1][L])); } inc(f[i], 1ll * i * fac[i] % mod); } printf("%d\n", g[n]); } int main() { cin >> n >> mod; init(); dp(); return 0; }