[ZJOI2015]地震后的幻想乡

Posted hnylmstea

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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]地震后的幻想乡的主要内容,如果未能解决你的问题,请参考以下文章

bzoj3925: [Zjoi2015]地震后的幻想乡

bzoj3925 [Zjoi2015]地震后的幻想乡

[ZJOI2015]地震后的幻想乡

P3343 [ZJOI2015]地震后的幻想乡

BZOJ 3925 [Zjoi2015]地震后的幻想乡 一道积分神题

Luogu P3343 [ZJOI2015]地震后的幻想乡