Solution -「ZJOI 2015」「洛谷 P3343」地震後的幻想鄉
阿新 • • 發佈:2022-03-14
\(\mathscr{Description}\)
Link.
給定連通圖簡單無向 \(G=(V,E)\),對於 \(e\in E\),有邊權 \(t_e\) 獨立均勻隨機生成自 \([0,1]\)。求 \(G\) 的生成樹最大邊權最小值的期望,不取模。
\(n\le10\)。
\(\mathscr{Solution}\)
\[\begin{align} E(\min_T\max_{e\in E_T}t_e) &= \int_0^1 P[\min_T\max_{e\in E_T}t_e>x]~\text dx\\ &= \int_0^1 P[\forall T,\exist e\in E_T,t_e>x]~\text dx\\ &= \int_0^1 P[G\text{ is not connected by }e\text{ whose }t_e<x]~\text dx\\ &= \int_0^1 \sum_{E_1\cup E_2=E,E_1\cap E_2=\varnothing}[G\text{ is not connected by }E_1]x^{|E_1|}(1-x)^{|E_2|}~\text dx\\ &= \sum_{E_1\cup E_2=E,E_1\cap E_2=\varnothing}[G\text{ is ...}]\int _0^1 x^{|E_1|}(1-x)^{|E_2|}~\text dx\\ &= \sum_{E_1\cup E_2=E,E_1\cap E_2=\varnothing}[G\text{ is ...}]\sum_{i=0}^{|E_2|}(-1)^i\binom{|E_2|}{i}\int_0^1 x^{|E_1|+i}~\text dx\\ &= \sum_{E_1\cup E_2=E,E_1\cap E_2=\varnothing}[G\text{ is ...}]\sum_{i=0}^{|E_2|}(-1)^i\binom{|E_2|}{i}\frac{1}{|E_1|+i+1}\\ &= \sum_{u_0\in S\subsetneq V}\sum_{i=0}^{\operatorname{inc}(S)}g(S,i)\sum_{j\ge t=i+\operatorname{cut}(S,V\setminus S)}\binom{\operatorname{inc}(V\setminus S)}{j-t}f(j). \end{align} \]以上是草稿,來解釋一下。
從 \((1)\rightarrow(7)\),其實就幹了一件事——把積分丟到簡單函式上消掉。畢竟我也只會求簡單函式的積分。\((8)\) 的目的是把邊的列舉更多的變為點的列舉。其中,\(f(|E_2|)\) 即對 \((7)\) 中第二層和式的換元,這個可以直接求。第一個和式的 \(u_0\in S\subsetneq V\) 是常見子集 DP trick,即列舉 \(E_1\) 在 \(G\) 下形成的包含某個特定結點的連通塊,\(g(S,i)\) 表示在 \(S\) 的誘導子圖內刪掉恰 \(i\) 條邊,使得子圖連通的方案數。子集 DP 一下可以 \(\mathcal O(3^nnm)\)
提示:2022 年了,選手可以使用 __int128
和 __float128
。
\(\mathscr{Code}\)
/*+Rainybunny+*/ #include <bits/stdc++.h> #define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i) #define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i) typedef __int128 LL; typedef __float128 LD; const int MAXN = 10, MAXM = 45; int n, m, adj[MAXN + 5], inc[1 << MAXN]; LD f[MAXM + 5]; LL bino[MAXM + 5][MAXM + 5], g[1 << MAXN][MAXM + 5]; inline void init() { bino[0][0] = 1; rep (i, 1, m) { bino[i][0] = 1; rep (j, 1, i) bino[i][j] = bino[i - 1][j - 1] + bino[i - 1][j]; } rep (i, 0, m) { rep (j, 0, i) { f[i] += (j & 1 ? -1. : 1.) * LD(bino[i][j]) / (m - i + j + 1.); } } rep (S, 1, (1 << n) - 1) { inc[S] = inc[S ^ (S & -S)] + __builtin_popcount(adj[31 - __builtin_clz(S & -S)] & S); } } int main() { scanf("%d %d", &n, &m); rep (i, 0, m - 1) { int u, v; scanf("%d %d", &u, &v); adj[u - 1] |= 1 << v >> 1, adj[v - 1] |= 1 << u >> 1; } init(); LD ans = 0.; rep (S, 0, (1 << n) - 2) if (S & 1) { rep (i, 0, inc[S]) g[S][i] = bino[inc[S]][i]; for (int T = (S - 1) & S; T; T = (T - 1) & S) if (T & 1) { int cut = inc[S] - inc[T] - inc[S ^ T]; rep (i, cut, inc[S]) { rep (j, 0, std::min(i - cut, inc[T])) { g[S][i] -= g[T][j] * bino[inc[S ^ T]][i - j - cut]; } } } int incT = inc[((1 << n) - 1) ^ S], cut = m - inc[S] - incT; rep (i, 0, inc[S]) { LD tmp = 0.; rep (j, i + cut, m - inc[S] + i) { tmp += bino[incT][j - i - cut] * f[j]; } ans += g[S][i] * tmp; } } printf("%Lf\n", (long double)ans); return 0; }