题意
给定 n n n个点, m m m条边的图 G G G,每条边 e i e_i ei是0~1的实数,求 G G G的最小生成树中边的最大值的期望。
n ≤ 10 n \le 10 n≤10, m ≤ n ( n − 1 ) / 2 m \le n(n-1)/2 m≤n(n−1)/2
题解
对于 n n n个 [ 0 , 1 ] [0,1] [0,1]之间的随机变量 x 1 , x 2 , … , x n x_1,x_2,…,x_n x1,x2,…,xn,第 k k k小的期望值是 k n + 1 \frac {k}{n+1} n+1k。
设 P S , i P_{S,i} PS,i表示点集 S S S选择 i i i条边不连通的概率。
刚好 i i i条边连通的概率是 ( 1 − P S , i ) − ( 1 − P S , i − 1 ) = P S , i − 1 − P S , i (1-P_{S,i}) - (1-P_{S, i-1}) = P_{S,i - 1}-P_{S, i} (1−PS,i)−(1−PS,i−1)=PS,i−1−PS,i
令总点集为 a l l all all
a n s = 1 m + 1 ( P a l l , 0 − P a l l , 1 ) + 2 m + 1 ( P a l l , 1 − P a l l , 2 ) + . . . + m + 1 m + 1 P a l l , m = 1 m + 1 ∑ i = 0 m P a l l , m \begin{aligned} ans &= \frac {1} {m + 1} (P_{all, 0} - P_{all, 1}) + \frac {2} {m+1} (P_{all, 1} - P_{all, 2}) + ... + \frac {m + 1} {m + 1} P_{all, m} \\ &= \frac {1} {m + 1} \sum_{i=0} ^{m} P_{all, m} \end{aligned} ans=m+11(Pall,0−Pall,1)+m+12(Pall,1−Pall,2)+...+m+1m+1Pall,m=m+11i=0∑mPall,m
如何计算 P P P:
求 P P P即求不连通的方案数除以总方案数。
设 f [ S ] [ i ] f[S][i] f[S][i]表示点集 S S S选择 i i i条边不连通的方案数, g [ S ] [ i ] g[S][i] g[S][i]表示点集 S S S选择 i i i条边连通的方案数。
P S , i = f [ S ] [ i ] ( i c n t [ S ] ) P_{S, i}=\frac {f[S][i]} {\binom {i} {cnt[S]}} PS,i=(cnt[S]i)f[S][i]
设 c n t [ S ] cnt[S] cnt[S]表示点集 S S S形成的子图的边数。
显然 f [ S ] [ i ] + g [ S ] [ i ] = ( i c n t [ S ] ) f[S][i]+g[S][i]=\binom{i}{cnt[S]} f[S][i]+g[S][i]=(cnt[S]i)
转移枚举包含某定点 S S S的真子集: f [ S ] [ i + j ] = ∑ T ⊂ S g [ T ] [ i ] ∗ ( j c n t [ S − T ] ) , i ∈ [ 0 , c n t [ T ] ] , j ∈ [ 0 , c n t [ S − T ] ] f[S][i+j] = \sum_{T \subset S}g[T][i] * \binom {j}{cnt[S - T]}, i \in [0, cnt[T]], j \in [0, cnt[S-T]] f[S][i+j]=∑T⊂Sg[T][i]∗(cnt[S−T]j),i∈[0,cnt[T]],j∈[0,cnt[S−T]]
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll ;
int e[15], sz[5010], cnt[5010] ;
ll c[50][50], f[5010][50], g[5010][50] ;
int main() {
int n, m ;
cin >> n >> m ;
for (int i = 1; i <= m; i ++) {
int u, v ;
scanf("%d%d", &u, &v) ;
u --; v -- ;
e[u] |= (1 << v) ;
e[v] |= (1 << u) ;
}
c[0][0] = 1 ;
for (int i = 1; i <= m; i ++) {
c[i][0] = c[i][i] = 1 ;
for (int j = 1; j < i; j ++)
c[i][j] = c[i - 1][j] + c[i - 1][j - 1] ;
}
for (int s = 1; s < (1 << n); s ++) {
sz[s] = sz[s >> 1] + (s & 1) ;
if (sz[s] == 1) {
g[s][0] = 1; continue ;
}
for (int i = 0; i < n; i ++)
if (s & (1 << i))
cnt[s] += sz[e[i] & s] ;
cnt[s] >>= 1 ;
int lowbit = s & (-s) ;
for (int t = (s - 1) & s; t; t = (t - 1) & s)
if (t & lowbit)
for (int i = 0; i <= cnt[t]; i ++)
for (int j = 0; j <= cnt[s ^ t]; j ++)
f[s][i + j] += g[t][i] * c[cnt[s ^ t]][j] ;
for (int i = 0; i <= cnt[s]; i ++) g[s][i] = c[cnt[s]][i] - f[s][i] ;
}
double ans = 0 ;
for (int i = 0; i <= m; i ++)
ans += (double)f[(1 << n) - 1][i] / c[cnt[(1 << n) - 1]][i] ;
ans /= (m + 1) ;
printf("%.6f\n", ans) ;
return 0 ;
}