不会积分的本蒟蒻真的想不出此题。
Address
https://www.lydsy.com/JudgeOnline/problem.php?id=3925
Solution
显然,如果已经知道了
条边的权值,那么可以将边按权值排序,和 Kruskal 一样,从小到大考虑每一条边,如果加入一条边后不形成环就加入这条边。树结构形成(有
条边被加入)时,最后加入的一条边(最大边)就是需要的修复时间。
根据 HINT 里的说明可以得出,我们只需要求出最大边在这
条边中排名的期望值,然后再除以
,就得到了修复时间的期望值。
设
表示按随机顺序加入前
条边,恰好把图连通的概率。
「恰好」的概念:前
条边加入时该图不连通,再加入第
条边后图连通。
接下来我们思考 的后缀和的实际意义。
容易发现, 表示至少加入 条边后图才连通的概率。
显然,这样等价于加入 条边后原图不连通的概率。
所以,问题转化为:
对于每个 ,求出随机选出 条边加入后,原图不连通的概率。
看到 的范围,可以猜出这是状压 DP :
表示在点集 和点集 中的点之间的边构成的子图中,选出 条边,不连通点集 的方案数。
表示在点集 和点集 中的点之间的边构成的子图中,选出 条边,连通点集 的方案数。
设上面所描述的子图的边数为 。
显然:
思路要点在转移。思路是:枚举点集 的第一个元素 所在的连通块(枚举 的一个非空真子集 , 必须包含 ,并将 作为这个连通块包含的点集),然后再枚举这个连通块包含的边数 (满足 )。那么点集 和点集 之间的任何一对点都不会有连边,并且点集 之间连了剩下的 条边。得出转移:
根据 就能求出 。
这样,
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Subset(k, U) for (k = (U - 1) & U; k; k = (k - 1) & U)
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
typedef long long ll; const int N = 12, M = 102, C = (1 << 10) + 5;
int n, m, Cm, orz[C], otz[C]; bool g[N][N]; ll f[M][C][2], _C[M][M]; double ans;
int main() {
int i, j, k, h; n = read(); m = read(); For (i, 1, m) g[read()][read()] = 1;
Cm = (1 << n) - 1; For (i, 1, Cm) {
orz[i] = 1; while (!((i >> orz[i] - 1) & 1)) orz[i]++;
For (j, 1, n) if ((i >> j - 1) & 1) For (k, 1, n)
if (((i >> k - 1) & 1) && g[j][k]) otz[i]++;
}
For (i, 0, m) _C[i][0] = 1; For (i, 1, m) For (j, 1, i)
_C[i][j] = _C[i - 1][j] + _C[i - 1][j - 1];
For (i, 0, n - 1) f[0][1 << i][1] = 1;
For (i, 0, Cm) f[0][i][0] = 1 - f[0][i][1]; For (i, 1, m) For (j, 1, Cm) {
Subset (k, j) if ((k >> orz[j] - 1) & 1) For (h, 0, i)
f[i][j][0] += f[h][k][1] * _C[otz[k ^ j]][i - h];
f[i][j][1] = _C[otz[j]][i] - f[i][j][0];
}
For (i, 0, m) ans += 1.0 * f[i][Cm][0] / _C[m][i];
printf("%.6lf\n", ans / (m + 1)); return 0;
}