bzoj3534 [Sdoi2014]重建

Description

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

Input

  输入的第一行包含整数N。
  接下来N行,每行N个实数,第i+l行,列的数G[i][j]表示城市i与j之
间仍有道路联通的概率。
    输入保证G[i][j]=G[j][i],且G[i][j]=0;G[i][j]至多包含两位小数。

Output

    输出一个任意位数的实数表示答案。
    你的答案与标准答案相对误差不超过10^(-4)即视为正确。

Sample Input

3
0 0.5 0.5
0.5 0 0.5
0.5 0.5 0

Sample Output

0.375

HINT

1 < N < =50

数据保证答案非零时,答案不小于10^-4

 

正解:矩阵树定理。

首先矩阵树定理的度数矩阵记录的是每个点的边权和,邻接矩阵记录的是边权,求的则是所有生成树的边权乘积和。

我们可以发现,一棵生成树的概率就是所有存在的边的存在概率乘不存在的边的不存在概率。

$(i,j)$的概率是$p(i,j)$,那么把$\frac{p(i,j)}{1-p(i,j)}$当成边权,最后再乘上所有的$1-p(i,j)$即可。

 

 1 #include <bits/stdc++.h>
 2 #define il inline
 3 #define RG register
 4 #define ll long long
 5 #define eps (1e-9)
 6 
 7 using namespace std;
 8 
 9 double g[55][55],ans;
10 int n;
11 
12 il void gauss(){
13   for (RG int i=1,id;i<n;++i){
14     for (id=i;id<n;++id) if (fabs(g[id][i])>eps) break;
15     if (id!=i) for (RG int j=1;j<n;++j) swap(g[i][j],g[id][j]);
16     RG double tmp=g[i][i],ret;
17     for (RG int j=i+1;j<n;++j){
18       if (fabs(g[j][i])<eps) continue; ret=g[j][i]/tmp;
19       for (RG int k=i;k<n;++k) g[j][k]-=g[i][k]*ret;
20     }
21     ans*=g[i][i];
22   }
23   ans=fabs(ans); return;
24 }
25 
26 int main(){
27 #ifndef ONLINE_JUDGE
28   freopen("rebuild.in","r",stdin);
29   freopen("rebuild.out","w",stdout);
30 #endif
31   cin>>n,ans=1;
32   for (RG int i=1;i<=n;++i)
33     for (RG int j=1;j<=n;++j){
34       scanf("%lf",&g[i][j]);
35       RG double tmp=fabs(1-g[i][j])>eps?(1-g[i][j]):eps;
36       if (i<j) ans*=tmp; g[i][j]/=tmp;
37     }
38   for (RG int i=1;i<=n;++i)
39     for (RG int j=1;j<=n;++j)
40       if (i!=j) g[i][i]-=g[i][j];
41   gauss(),printf("%0.9lf\n",ans); return 0;
42 }

 

posted @ 2018-02-27 08:49  wfj_2048  阅读(214)  评论(0编辑  收藏  举报