[ZJOI2015]地震后的幻想乡
给定一张图,每条边的边权在 ([0,1]) 中随机,求最小生成树的最大边权的期望。
(nle 10,mle frac{n(n-1)}{2})
Solution
这个题大概代表了七月份的一点学习记录,今天准备整理一下。不过按照这个方法推下来好 easy(无脑) 啊
对于连续型随机变量,我们一般使用概率密度函数 (f(x)) 来描述它取值的概率,其一定区域的积分可以表示取值为连续的一段区间的概率。
例如,论证 (mathbb{E}(max{x_1,x_2...x_n})=frac{n}{n+1}) 时,我们先定义 (F(x)=max{x_1...x_n}le x),那么容易得到 (F(x)=x^n)
我们不难观察到,(F(x)) 的导数就是其对应的密度函数 (f(x))
所以我们得到 (f(x)=x^{n-1}n)
容易观察得到,我们计算的期望为:
接下来我们考虑证明题面给出的性质,注意到我们计算的是集合的 (min_k),所以我们可以考虑通过拓展 (min-max) 容斥来计算答案:
设 (m=n-k)
于是得证。
最后一步是分部积分的结论,下面也给出证明:
考虑:
设 (B(a+b,b)=int_0^1 x^a(1-x)^b),那么不难发现:
- (f(x)=frac{x^a}{a+1},g(x)=(1-x)^b)
观察到 (B(a+b,0)=int_0^1 x^{a+b}=frac{1}{a+b+1}),不难得到 (B(a+b,b)=frac{b!}{(a+b+1)^{underline{b+1}}}=frac{a!b!}{(a+b+1)!})
至此,命题得证。
注意到答案是:
构造 (g(x)=P(Xle x)),那么 (P(X=x)) 为 (g(x)) 的导数。
于是答案即为:
不难发现答案即为:
后者等价于仅保留小于 (x) 的边有此图联通。
可以基于这样的考量,我们给每条边赋予 (0/1) 的权值表示他是否小于等于 (x),如果为 (1) 那么小于等于 (x),不妨假设有 (a) 条 (1) 边和 ((m-a)) 条 (0) 边,此时概率角度的贡献为:
对于某个具体的 (a),我们相当于计算:
于是我们成功的再次通过分部积分将此问题转换为给边染色,恰好染 (a) 条黑色边使得这张图联通的方案数。
等价于求这张图的恰好 (a) 条边的联通子图的数量。
经典的容斥手段是对于子集 (S) 先任意保留边,在枚举 (1) 号点所在的连通块,这里我们还需要枚举 (1) 所在的连通块所保留的边数,此时外部的边数是固定的,然后转移,复杂度为 (mathcal O(3^ncdot m^2))。
不难发现转移的时候的卷积为子集卷积,可以通过 FWT 处理做到 (mathcal O(2^nn^2cdot m^2)),不过意义不大。
(Code:)
#include<bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
int gi() {
char cc = getchar() ; int cn = 0, flus = 1 ;
while( cc < '0' || cc > '9' ) { if( cc == '-' ) flus = - flus ; cc = getchar() ; }
while( cc >= '0' && cc <= '9' ) cn = cn * 10 + cc - '0', cc = getchar() ;
return cn * flus ;
}
const int N = 10 + 5 ;
const int M = 60 ;
int n, m, lim, g[1 << 11] ;
long double f[1 << 11][M], C[M][M], fac[M] ;
signed main()
{
n = gi(), m = gi() ; int x, y ;
rep( i, 1, m )
x = gi() - 1, y = gi() - 1,
++ g[(1 << x) | (1 << y)] ;
lim = (1 << n) ;
for(re int k = 1; k < lim; k <<= 1 )
rep( i, 0, lim ) if(i & k) g[i] += g[i ^ k] ;
C[0][0] = 1, f[0][0] = 1, fac[0] = 1 ;
rep( i, 1, m ) rep( j, 0, i ) C[i][j] = (!j) ? 1 : C[i - 1][j - 1] + C[i - 1][j] ;
rep( i, 1, m + 1 ) fac[i] = fac[i - 1] * i ;
for(re int S = 1; S < lim; ++ S) {
int p = n ;
rep( j, 0, n - 1 ) if((1 << j) & S) { p = j ; break ; }
rep( j, 0, m ) f[S][j] = C[g[S]][j] ;
for(re int i = S; i; i = (i - 1) & S) {
if((!(i & (1 << p))) || (i == S)) continue ;
int u = S ^ i, l = g[i], r = g[u] ;
rep( j, 0, l ) rep( k, 0, r )
f[S][j + k] -= f[i][j] * C[r][k] ;
}
}
long double Ans = 1 ; -- lim ;
rep( j, 0, m ) Ans -= f[lim][j] / fac[m + 1] * fac[j] * fac[m - j] ;
printf("%.6Lf
", Ans ) ;
return 0 ;
}