#樹形dp#C 樹上排列
阿新 • • 發佈:2020-11-25
分析
設\(dp[x][i]\)表示以\(x\)為根的子樹中\(x\)的排名為\(i\)的方案數,
然後列舉子節點轉移即可,Talk is cheap,Show me the code
程式碼
#include <cstdio> #include <cctype> #define rr register using namespace std; const int N = 3011, mod = 998244353; struct node { int y, w, next; } e[N << 1]; int siz[N], dp[N][N], f[N], as[N], fac[N], inv[N], n, et = 1, ans; inline signed iut() { rr int ans = 0; rr char c = getchar(); while (!isdigit(c)) c = getchar(); while (isdigit(c)) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar(); return ans; } inline signed mo(int x, int y) { return x + y >= mod ? x + y - mod : x + y; } inline signed C(int n, int m) { return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod; } inline void dfs(int x, int fa) { siz[x] = dp[x][1] = 1; for (rr int i = as[x]; i; i = e[i].next) if (e[i].y != fa) { dfs(e[i].y, x); for (rr int j = 1; j <= siz[x]; ++j) f[j] = dp[x][j], dp[x][j] = 0; for (rr int j = 1; j <= siz[x]; ++j) { rr int sum = 0; if (e[i].w) for (rr int o = siz[e[i].y]; ~o; --o) dp[x][j + o] = mo(dp[x][j + o], 1ll * f[j] * C(j + o - 1, j - 1) % mod * C(siz[x] + siz[e[i].y] - j - o, siz[x] - j) % mod * sum % mod), sum = mo(sum, dp[e[i].y][o]); else for (rr int o = 0; o <= siz[e[i].y]; ++o) sum = mo(sum, dp[e[i].y][o]), dp[x][j + o] = mo(dp[x][j + o], 1ll * f[j] * C(j + o - 1, j - 1) % mod * C(siz[x] + siz[e[i].y] - j - o, siz[x] - j) % mod * sum % mod); } siz[x] += siz[e[i].y]; } } signed main() { freopen("perm.in", "r", stdin); freopen("perm.out", "w", stdout); n = iut(), fac[0] = fac[1] = inv[0] = inv[1] = 1; for (rr int i = 2; i <= n; ++i) inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod; for (rr int i = 2; i <= n; ++i) fac[i] = 1ll * fac[i - 1] * i % mod; for (rr int i = 2; i <= n; ++i) inv[i] = 1ll * inv[i - 1] * inv[i] % mod; for (rr int i = 1; i < n; ++i) { rr int x = iut(), y = iut(); e[++et] = (node){ y, 1, as[x] }, as[x] = et; e[++et] = (node){ x, 0, as[y] }, as[y] = et; } dfs(1, 0); for (rr int i = 1; i <= n; ++i) ans = mo(ans, dp[1][i]); return !printf("%d", ans); }