题目大意
给出一个图,每条边都有一定概率出现,问最后出现一棵树的概率。
题解
我们平时矩阵树定理所求的就是\(\displaystyle \sum_{T} \prod_{e \in T} Val_e\)
其中\(T\)是树,\(e\)是边。
这道题我们要求的就是 \(\displaystyle \sum_{T} (\prod_{e \in T} Val_e \prod_{e \notin T}(1 - Val_e))\)
尝试把\(e \notin 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;
}