解题:SDOI 2014 重建

题面

做这个这个题需要稍微深入理解一点矩阵树定理:套矩阵树定理得到的东西是有意义的,它是“所有生成树边权乘积之和”(因为度数矩阵是点的边权和,邻接矩阵是边权),即$\sum_{t}\prod_{e∈t}v_e$,其中t是生成树集合中的树。

但是这题同时还要保证不该出现的不要出现,所以答案其实是$\sum_{t}(\prod_{e∈t}v_e\prod_{e'∉t}(1-v_{e'}))$

怎么做?我们尝试推一波把它变成矩阵树定理的形式再套

$ans=\sum_{t}(\prod_{e∈t}v_e\prod_{e'∉t}(1-v_{e'}))$

$=\sum_{t}(\prod_{e∈t}v_e\frac{\prod (1-v_e)}{\prod_{e∈t}(1-v_e)})$

$=\prod (1-v_e)(\sum_{t}\prod_{e∈t}\frac{v_e}{1-v_e})$

注意可能会有不出现/必定出现的边,没法做除法,怎么办?

我们给他一个极小的边权,保证不影响结果,因为答案精度不是十分高=。=

 1 #include<cmath>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define double long double
 6 using namespace std;
 7 const int N=55;
 8 const double eps=1e-10;
 9 double rd,mul,equ[N][N]; int n;
10 void Correct(double &x)
11 {
12     if(x<=eps) x=eps;
13     if(1-x<=eps) x=1-eps;
14 }
15 void Guass()
16 {
17     for(int i=1;i<=n;i++)
18     {
19         int tmp=i;
20         for(int j=i+1;j<=n;j++)    
21             if(fabs(equ[j][i])>fabs(equ[tmp][i])) tmp=j;
22         for(int j=i;j<=n;j++)
23             swap(equ[i][j],equ[tmp][j]);
24         for(int j=1;j<=n;j++)
25             if(i!=j)
26             {
27                 double tep=equ[j][i]/equ[i][i];
28                 for(int k=i;k<=n;k++)
29                     equ[j][k]-=tep*equ[i][k];
30             }
31     } 
32 }
33 int main()
34 {
35     scanf("%d",&n),mul=1;
36     for(int i=1;i<=n;i++)
37         for(int j=1;j<=n;j++)
38         {
39             scanf("%Lf",&rd),Correct(rd);
40             if(i<j) mul*=(1-rd); equ[i][j]=rd/(1-rd);
41         }
42     for(int i=1;i<=n;i++)
43     {
44         equ[i][i]=0;
45         for(int j=1;j<=n;j++)
46             if(i!=j) equ[i][i]-=equ[i][j];
47     }
48 //    for(int i=1;i<=n;i++,puts(""))
49 //        for(int j=1;j<=n;j++)
50 //            printf("%.2Lf ",equ[i][j]);
51     n--,Guass();
52     for(int i=1;i<=n;i++) mul*=equ[i][i];
53     printf("%.5Lf",abs(mul));
54     return 0;
55 }
View Code

 

posted @ 2019-02-26 21:51  Speranza_Leaf  阅读(101)  评论(0)    收藏  举报