BZOJ3925 [ZJOI2015] 地震后的幻想乡
Description
一个 $n$ 个结点、 $m$ 条边的无向图,所有边权在 $[0, 1]$ 之间均匀分布(互相独立);求这个图的最小生成树上的最大边权的期望值。
$n \leqslant 8, m \leqslant \frac{n(n-1)}2$ 。
Solution
显然,题目等价于同时修建这$m$条道路时,首次连通的时间期望值。
我们令$p(x)$为首次连通的时间(设它为随机变量$X$)的概率分布函数,也就是$p(x)=\frac{dP(x)}{dx}$,其中$P(t)=Pr(X\geq t)$,那么根据期望的定义有
$$\begin{aligned}E(X)&=\int_0^1p(x)x\mathrm{d}x\\&=\int_0^1p(x)\int_0^x\mathrm{d}t\,\mathrm{d}x\\&=\int_0^1\int_t^1p(x)\mathrm{d}x\,\mathrm{d}t\\&=\int_0^1P(t)\mathrm{d}t\end{aligned}$$
所以我们只需计算$\int_0^1P(t)dt$即可。
考虑如果$X\geq t$,也就是说图在$t$时刻不连通,那么我们可以枚举结点$1$在$t$时刻的连通块里有哪些点。如果有$S(1 \in S)$这些点,那么首先$S$要连通(概率我们定义为$1-P_S(t)$),其次,$S$里的点和其它点之间的边的$e$都大于$t$(概率为$(1-t)^{T(S, \overline S)}$,其中$\overline S$为$S$的补集,$T(A, B)$表示点集$A,B$之间的边数);由于这两个事件是独立的,所以总的概率是$(1-t)^{T(S, \overline S)}P_S(t)$。$P_S(t)$也可以类似计算,递推式为:
$$P_S(t)=\sum_{1\in S_0 \subsetneq S}(1-t)^{T(S_0, S - S_0)}\left[1-P_{S_0}(t)\right]\qquad(1\in S)$$
边界条件是$P_{\{1\}}(t)=0$,因为单一的点永远是连通的。
那么有
$$\begin{aligned}\int_0^1P_S(t)\mathrm{d}t&=\sum_{1\in S_0 \subsetneq S}\int_0^1(1-t)^{T(S_0, S - S_0)}\left[1-P_{S_0}(t)\right]\mathrm{d}t\\&=\sum_{1\in S_0 \subsetneq S}\left[\int_0^1(1-t)^{T(S_0, S - S_0)}-(1-t)^{T(S_0, S - S_0)}P_{S_0}(t)\mathrm{d}t\right]\\&=\sum_{1\in S_0 \subsetneq S}\left[\frac{1}{1+T(S_0, S - S_0)}-\int_0^1(1-t)^{T(S_0, S - S_0)}P_{S_0}(t)\mathrm{d}t\right]\end{aligned}$$
同理,
$$\int_0^1(1-t)^kP_S(t)\mathrm{d}t=\sum_{1\in S_0 \subsetneq S}\left[\frac{1}{1+k+T(S_0, S - S_0)}-\int_0^1(1-t)^{k+T(S_0, S - S_0)}P_{S_0}(t)\mathrm{d}x\right]$$
边界条件:$\int_0^1P_{\{1\}}(t)(1-t)^k{\rm d}t=\int_0^10{\rm d}t=0$
而我们要求的就是
$$EX = \int_0^1P(t){\rm d}t =\int_0^1(1-t)^0P_{\{1,2,\dots,n\}}(t){\rm d}t$$
于是关于$S, k$递推即可。按$S$从小到大计算,每次枚举子集之后利用位运算$O(n)$求出$T$,总时间复杂度$O(3^nm)$。
Code
#include <algorithm>
#include <cstdio>
#include <cstring>
const int N = 10;
const int M = 45 + 1;
bool vis[1 << N][M];
int siz[1 << N], link[N];
double f[1 << N][M];
int main() {
int n, m;
("%d%d", &n, &m);
scanfint lim = 1 << n;
for (int i = 0, x, y; i < m; ++i) {
("%d%d", &x, &y);
scanf--x; --y;
[x] |= (1 << y);
link[y] |= (1 << x);
link}
for (int S = 1; S < lim; ++S) siz[S] = siz[S & (S - 1)] + 1;
for (int S1 = 3; S1 < lim; ++S1) if (S1 & 1) {
for (int S2 = (S1 - 1) & S1; S2 != 0; S2 = (S2 - 1) & S1) if (S2 & 1) {
int T = 0;
for (int i = 0; i < n; ++i) if ((S1 >> i) & (~S2 >> i) & 1)
+= siz[link[i] & S2];
T for (int i = 0; i + T <= m; ++i)
[S1][i] += 1.0 / (i + T + 1) - f[S2][i + T];
f}
}
("%.6lf\n", f[lim - 1][0]);
printfreturn 0;
}