题目大意
给出一个图,每条边都有一定概率出现,问最后出现一棵树的概率。
题解
我们平时矩阵树定理所求的就是(displaystyle sum_{T} prod_{e in T} Val_e)
其中(T)是树,(e)是边。
这道题我们要求的就是 (displaystyle sum_{T} (prod_{e in T} Val_e prod_{e otin T}(1 - Val_e)))
尝试把(e otin T)这一部分容斥掉 (displaystyle sum_{T} (prod_{e in T} Val_e frac{prod_{e}(1 - Val_e)}{prod_{e in T}(1 - Val_e)}))
提出来一部分 (displaystyle prod_{e}(1 - Val_e) sum_{T} (prod_{e in T} frac{Val_e }{1 - Val_e}))
右边又变成了我们熟悉的形式,矩阵树定理解决.
#include<iostream>
#include<cstdio>
#define DB double
using namespace std;
int n;
const int N = 70;
const DB eps = 1e-7;
DB ans = 1.0;
DB d[N][N];
inline DB jue(DB x) {return x > 0 ? x : -x;}
void work()
{
DB inv, tmp;
for (int i = 2; i <= n; ++i)
{
for (int j = i + 1; j <= n; ++j)
if (!d[i][i] && d[j][i]) {ans = -ans; swap(d[i], d[j]); break;}
inv = 1.0 / d[i][i];
for (int j = i + 1; j <= n; ++j)
{
tmp = d[j][i] * inv;
for (int k = i; k <= n; ++k)d[j][k] = d[j][k] - d[i][k] * tmp;
}
}
for (int i = 2; i <= n; ++i)ans *= d[i][i];
}
int main()
{
cin >> n;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)scanf("%lf", &d[i][j]);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
{
if (jue(d[i][j]) < eps)d[i][j] = eps;
else if (jue(1.0 - d[i][j]) < eps)d[i][j] = 1.0 - eps;
if (j > i)ans *= (1.0 - d[i][j]);
d[i][j] /= (1.0 - d[i][j]);
}
for (int i = 1; i <= n; ++i)
{
d[i][i] = 0;
for (int j = 1; j <= n; ++j)
if (i != j)d[i][i] += d[i][j], d[i][j] = -d[i][j];
}
work();
printf("%0.6f", ans);
return 0;
}