[ZJOI2015]地震後的幻想鄉
題面
給一個圖,邊權為 \([0,1]\) 的均勻分布的隨機實數,求期望最小生成樹的最大邊權。
提示:對於 \(n\) 個 \([0,1]\) 之間的隨機變量 \(x_1,x_2,\dots,x_n\) ,第 \(k\) 小的那個的期望值是\(\frac{k}{(n+1)}\)。
\(\text{Solution:}\)
將題意寫成數學公式,
在 \(Kruscal\) 的過程中求:
\[
E = \sum_{i=1}^m\frac{i\times P(加入第i條邊恰好連通)}{m+1}
\]
提出上面的式子:
\[
\begin{aligned}
E\times (m+1) &= \sum_{i=1}^mi\times P(加入第i條邊恰好連通)\&=\sum_{i=1}^mi\times (P(加入i條邊使圖連通)-P(加入i-1條邊使原圖連通))\&=\sum_{i=1}^mi\times (1-P(加入i條邊使圖不連通)-1+P(加入i-1條邊使原圖不連通))\&=\sum_{i=1}^mi\times (P(加入i-1條邊使原圖不連通) - P(加入i條邊使圖不連通))\\end{aligned}
\]
展開,得:
\[ E\times (m+1) = \sum_{i=0}^{m-1}P(加入i條邊使圖不連通) - m\times P(加入m條邊使圖不連通) \]
由於 \(m\times P(加入m條邊使圖不連通) = 0\) (題目保證連通)
\[ E\times (m+1) = \sum_{i=0}^{m-1}P(加入i條邊使圖不連通) \]
設 \(f[S][i]\) 為 \(G(S)\) 中選 \(i\) 條邊使原圖不連通的方案數.
\(g[S][i]\) 為 \(G(S)\) 中選 \(i\) 條邊使原圖連通的方案數.
\(ecnt[S]\) 為 \(G(S)\) 中所包含的邊數.
然後就可以轉移了
首先顯然有: \(g[S][i] = C_{ecnt[S]}^{i} - f[S][i]\)
由於聯通性的定義為任意兩點可以到達,我們考慮欽定一個點,枚舉它所能到的點集 \(T\) ,以及它不能到達的點集 \(\complement_{S}^T\), 有轉移:
\[ f[S][i] = \sum_{T\subsetneqq S} \sum_{j=0}^{ecnt[T]} g[T][j] \times C_{ecnt[\complement_{S}^T]}^{i-j} \]
然後就做完了。
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> using namespace std; #define LL long long #define debug(...) fprintf(stderr, __VA_ARGS__) #define GO debug("GO\n") inline int rint() { register int x = 0, f = 1; register char c; while (!isdigit(c = getchar())) if (c == '-') f = -1; while (x = (x << 1) + (x << 3) + (c ^ 48), isdigit(c = getchar())); return x * f; } const int N = 12, M = 55; int n, m; LL graph[N], cnt[1 << N], ecnt[1 << N], f[1 << N][M], g[1 << N][M]; LL C[M][M]; void init() { for (int i = 0; i <= m; ++ i) { C[i][0] = 1; for (int j = 1; j <= i; ++ j) C[i][j] = C[i - 1][j] + C[i - 1][j - 1]; } } int main() { #ifndef ONLINE_JUDGE freopen("xhc.in", "r", stdin); freopen("xhc.out", "w", stdout); #endif n = rint(), m = rint(); init(); for (int i = 0; i < m; ++ i) { int u = rint(), v = rint(); u--, v--; graph[u] |= (1 << v); graph[v] |= (1 << u); } for (int i = 0; i < 1 << n; ++ i) { for (int j = 0; j < n; ++ j) if (i >> j & 1) ecnt[i] += __builtin_popcount(graph[j] & i); ecnt[i] >>= 1;//每條邊算了兩遍 } for (int S = 0; S < 1 << n; ++ S) { if (__builtin_popcount(S) == 1) { g[S][0] = 1; continue; } for (int T = (S - 1) & S; T; T = (T - 1) & S)//枚舉真子集 if (T & (S & -S)) {//聯通的定義是兩兩之間互相可以到達,欽定一個點x,枚舉它能到達的點集(T),以及它不能到達的點(S ^ T),這裏欽定為lowbit for (int i = 0; i <= ecnt[T] + ecnt[S ^ T]; ++ i) { for (int j = 0; j <= min(ecnt[T], 1ll * i); ++ j) f[S][i] += g[T][j] * C[ecnt[S ^ T]][i - j]; } } for (int i = 0; i <= ecnt[S]; ++ i) g[S][i] = C[ecnt[S]][i] - f[S][i]; } double ans = 0; for (int i = 0; i < m; ++ i) ans += 1.0 * f[(1 << n) - 1][i] / C[m][i]; printf("%.6lf\n", ans / (m + 1)); }
[ZJOI2015]地震後的幻想鄉