题目
题目链接: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;
}