bzoj3925 ZJOI2015 地震后的幻想乡

题意

给定 n n n个点, m m m条边的图 G G G,每条边 e i e_i ei是0~1的实数,求 G G G的最小生成树中边的最大值的期望。

n ≤ 10 n \le 10 n10, m ≤ n ( n − 1 ) / 2 m \le n(n-1)/2 mn(n1)/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} (1PS,i)(1PS,i1)=PS,i1PS,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,0Pall,1)+m+12(Pall,1Pall,2)+...+m+1m+1Pall,m=m+11i=0mPall,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]=TSg[T][i](cnt[ST]j),i[0,cnt[T]],j[0,cnt[ST]]


代码

#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 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值