【洛谷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;
}