天天看點

[bzoj 3534][Sdoi2014] 重建

傳送門

Description

 T國有N個城市,用若幹雙向道路連接配接。一對城市之間至多存在一條道路。

在一次洪水之後,一些道路受損無法通行。雖然已經有人開始調查道路的損毀情況,但直到現在幾乎沒有消息傳回。

幸運的是,此前T國政府調查過每條道路的強度,現在他們希望隻利用這些資訊估計災情。具體地,給定每條道路在洪水後仍能通行的機率,請計算仍能通行的道路恰有N-1條,且能聯通所有城市的機率。

Solution

給出每條邊出現的機率,求生成一棵樹的機率。

首先要知道矩陣樹定理和變元矩陣樹定理。。。

對于一個無向圖G,它的生成樹個數等于其基爾霍夫Kirchhoff矩陣任何一個N-1階主子式的行列式的絕對值

基爾霍夫Kirchhoff矩陣 K =度數矩陣 D - 鄰接矩陣 A

這裡鄰接矩陣可以有不同形式

  • 如果\(A[i][j]\)表示i\(i\)和\(j\)之間邊的數量,則\(det\)等于生成樹的數量
  • 如果\(A[i][j]\)表示\(i\)和\(j\)之間邊的長度,則\(det=\sum_{T} \prod T_{e_i}\),也就是每個生成樹的邊權積之和

怎麼求行列式?

講得很淺顯的部落格

這裡有幾個性質:

  • 交換兩行(列),行列式變号
  • 加上另外一行的\(k\)倍,行列式不變
是以用高斯消元,把它變成一個上三角矩陣,那麼它的行列式就是對角線的乘積啦

Code 

#include <bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) (x>0?x:-x)
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
}
#define eps (1e-8)
int n;
double ans=1.,a[55][55];

double Gauss()
{
    double ret=1.;
    register int i,j,k;
    for(i=1;i<n;++i)
    {    
        //for(j=i+1;j<n;++j)
        //    if(abs(a[j][i])>abs(a[i][i])) std::swap(a[j],a[i]),ret=-ret;
        for(j=i+1;j<n;++j)
        {
            double t=a[j][i]/a[i][i];
            for(k=i;k<n;++k) a[j][k]-=t*a[i][k];
        }
        ret*=a[i][i];
    }
    return abs(ret);
}

int main()
{
    scanf("%d",&n);
    register int i,j;
    for(i=1;i<=n;++i)for(j=1;j<=n;++j)
    {
        scanf("%lf",&a[i][j]);
        double t=fabs(1.-a[i][j])<eps?eps:(1.-a[i][j]);
        if(i<j) ans*=t;
        a[i][j]=a[i][j]/t;
    }
    for(i=1;i<=n;++i)for(j=1;j<=n;++j)
        if(i!=j) a[i][i]+=a[i][j],a[i][j]=-a[i][j];
    printf("%.10lf\n",Gauss()*ans);
    return 0;
}                

Blog來自PaperCloud,未經允許,請勿轉載,TKS!

轉載于:https://www.cnblogs.com/PaperCloud/p/10214423.html