【洛谷P3317】重建

题目

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

思路

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

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

但是不属于树边那部分不容易搞,所以很自然的想到先令 \(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;
}
posted @ 2021-01-05 10:00  stoorz  阅读(71)  评论(0编辑  收藏  举报