【洛谷P3317】重建

题目

题目链接:https://www.luogu.com.cn/problem/P3317
T 国有 (N) 个城市,用若干双向道路连接。一对城市之间至多存在一条道路。
在一次洪水之后,一些道路受损无法通行。虽然已经有人开始调查道路的损毁情况,但直到现在几乎没有消息传回。
幸运的是,此前 T 国政府调查过每条道路的强度,现在他们希望只利用这些信息估计灾情。具体地,给定每条道路在洪水后仍能通行的概率,请计算仍能通行的道路恰有 (N-1) 条,且能联通所有城市的概率。
(nleq 50)

思路

连刷三道板子,真 TM 刺激。
假设最终的生成树为 (mathrm{T}),显然其贡献为

[Pi^{n}_{i=1}Pi^{n}_{j=i+1}left{egin{matrix}p_{i,j},(i,j)inmathrm{T} \(1-p_{i,j}),(i,j)∉mathrm{T}end{matrix} ight. ]

但是不属于树边那部分不容易搞,所以很自然的想到先令 (sum=Pi^{n}_{i=1}Pi^n_{j=i+1}(1-p_{i,j})),然后每一条边的 (p_{i,j}gets frac{p_{i,j}}{(1-p_{i,j})})。最终用矩阵树定理算出来的答案乘上 (sum) 即可。
注意当 (p_{i,j}=1) 时需要特判。为了防止除以 (0),我们就让 (p_{i,j}) 减去 (mathrm{eps}) 就可以了。本题精度要求较低可以通过。
时间复杂度 (O(n^3))

代码

#include <bits/stdc++.h>
using namespace std;

const int N=55;
const double eps=1e-12;
int n;
double sum,g[N][N];

double dit()
{
	double res=1,f=1;
	for (int i=1;i<n;i++)
	{
		for (int j=i;j<n;j++)
			if (fabs(g[j][i])>eps)
			{
				if (i!=j) f=-f;
				for (int k=1;k<n;k++)
					swap(g[j][k],g[i][k]);
				break;
			}
		if (fabs(g[i][i])<=eps) return 0;
		for (int j=i+1;j<n;j++)
			if (fabs(g[j][i])>eps)
			{
				double base=g[j][i]/g[i][i];
				for (int k=1;k<n;k++)
					g[j][k]-=g[i][k]*base;
			}
	}
	for (int i=1;i<n;i++) res*=g[i][i];
	return f*res;
}

int main()
{
	scanf("%d",&n);
	sum=1;
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
		{
			scanf("%lf",&g[i][j]);
			if (i>j)
			{
				if (g[i][j]==1.0) g[i][j]-=eps;
				sum*=(1-g[i][j]);
				g[j][i]=g[i][j]=-g[i][j]/(1-g[i][j]);
			}
		}
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			if (i!=j) g[i][i]-=g[i][j];
	printf("%.12lf",sum*dit());
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/14234121.html