1. 程式人生 > >BZOJ 3534: [Sdoi2014]重建(Matrix Tree)

BZOJ 3534: [Sdoi2014]重建(Matrix Tree)

傳送門

解題思路

  比較容易看的出來矩陣樹定理。然後就怒送一Wa,這個矩陣樹定理是不能直接用的。題目要求的其實是這個玩意。
\[ ans=\sum\limits_{Tree}( \prod\limits_{e\in Tree}p_e*\prod\limits_{e\notin Tree}(1-p_e)) \]
而矩陣樹能求的東西本質上其實是每棵生成樹的積的和,說人話就是這個。
\[ now=\sum\limits_{Tree}\prod\limits_{e\in Tree}w_e \]
這個形式跟上面那個很像,但還是有點不一樣。我們考慮將上面那個式子化簡。根據
\[ \prod\limits_{e\notin Tree}(1-p_e)=\frac{\prod\limits_e (1-p_e)}{\prod\limits_{e\in Tree}(1-p_e)} \]


把這玩意往最上面那個式子裡一帶,神奇的事情發生了:
\[ ans=\prod\limits_e(1-p_e)*\sum\limits_{Tree} \frac{\prod\limits_{e\in Tree}p_e}{\prod\limits_{e\in Tree}(1-p_e)} \]
前面這個玩意可以直接算出來。後頭這個玩意直接上矩陣樹,把鄰接矩陣的邊權改成\(\frac{p_e}{1-p_e}\)就行了。

通過這道題,讓我們明白了原來矩陣樹裡的那個邊權是可以自己規定的,算出來的結果為每個生成樹的積之和。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>

using namespace std;
const int MAXN = 55;
const double eps = 1e-8;

int n;
double ans=1.0,base=1.0,f[MAXN][MAXN];

inline void Matrix_tree(){
    double t;int p;
    for(int i=1;i<n;i++){
        p=i;
        for(int j=i+1;j<n;j++)
            if(fabs(f[p][i])<fabs(f[j][i])) p=j;
        if(p!=i) swap(f[i],f[p]);
        for(int j=i+1;j<n;j++){
            t=f[j][i]/f[i][i];
            for(int k=i;k<n;k++) 
                f[j][k]-=t*f[i][k];
        }
        ans*=f[i][i];
    }
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            scanf("%lf",&f[i][j]);if(i==j) continue;
            if(f[i][j]>1.0-eps) f[i][j]-=eps;
            if(i>j && f[i][j]>eps) base*=(1-f[i][j]);
            f[i][j]=f[i][j]/(1-f[i][j]);
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)if(i!=j)
            f[i][i]+=f[i][j],f[i][j]=-f[i][j];
    Matrix_tree();printf("%.10lf",ans*base);
    return 0;
}