BZOJ 3925 [Zjoi2015]地震后的幻想乡

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3925

题意:给定一个 n m边的无向图,没有重边和自环,每条边的权值为 [0,1] 之间的随机变量,求最小生成树中最大边的期望权值。
n10,mn(n1)2

题解:根据期望的线性性,我们可以算出随机选前 k 小的那些边使图恰好连通的概率,则答案为概率之和除以(m+1),因为对于 m [0,1]之间的随机变量 x1,x2,...,xm ,第 k 小的那个的期望值是km+1
此题略卡精度,所以我们可以考虑先算出可行方案数,再除以总方案数得到概率。
cnt[i] 表示点集 i 之间的无向边边数,f[i][j]表示点集为 i ,选用j条边使点集不连通的方案数, g[i][j] 表示点集为 i ,选用j条边使点集连通的方案数,那么显然有 f[i][j]+g[i][j]=(jcnt[i]) ,而计算 f 的方法,可以是通过枚举包含点集里某个定点的连通块大小来不重不漏的计数。
对于计算f[S],考虑包含点集 S 中某个定点P的点集 T ,则T S 的真子集且T ST 之间没有连边,就可以不重不漏地代表 S 不连通的所有情况,但是两个子点集的边数任意,只用保证T是连通的,那么就有

f[S][i+j]=TSg[T][i](jcnt[ST])

这个转移可以通过对子集的枚举做到 O(3nm)
令全集为 all ,于是答案为
1m+1i=0mf[all][i](icnt[all])

这样做用 double就可以保证精度了,无需 __float128

代码:

#include <cstdio>
const int maxn = 11, maxm = 46;
int n, m, e[maxn], sz[1 << maxn], cnt[1 << maxn];
long long c[maxm][maxm], f[1 << maxn][maxm], g[1 << maxn][maxm];
double ans;
int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 0; 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 - 1] + c[i - 1][j];
    }
    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 >> i) & 1)
                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];
    }
    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;
}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值