洛谷 P3317 [SDOI2014]重建 矩阵树定理

题目大意

给出一个图,每条边都有一定概率出现,问最后出现一棵树的概率。

题解

我们平时矩阵树定理所求的就是\(\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;
}
posted @ 2020-03-28 21:02  wljss  阅读(144)  评论(0编辑  收藏  举报