矩阵树定理

构造一个拉普拉斯矩阵:对于边 ( u , v ) (u,v) (u,v),矩阵 a [ u ] [ u ] a[u][u] a[u][u]++, a [ v ] [ v ] a[v][v] a[v][v]++, a [ u ] [ v ] a[u][v] a[u][v]–, a [ v ] [ u ] a[v][u] a[v][u]–,去掉最后一行最后一列,求行列式(取模用辗转相除),即图的生成树个数

矩阵树求的是: ∑ T ∏ e ∈ T p e \sum_T \prod_{e\in T} p_e TeTpe,生成树构成的边的概率乘积,再累加

P4111 [HEOI2015] 小 Z 的房间
板子 注意要去掉最后一行最后一列

#include 
#define ll long long
const int N=105;
const ll mod=1e9;
int cnt,flag=0;
ll a[N][N];
void Gauss(){
    for (int i=1;i<=cnt;i++){
        for (int j=i+1;j<=cnt;j++){
            while (a[j][i]){
                ll use=a[i][i]/a[j][i];
                for (int k=1;k<=cnt;k++){
                    a[i][k]=(a[i][k]-use*a[j][k]%mod+mod)%mod;
                }
                std::swap(a[i],a[j]);
                flag^=1;
            }
        }
    }
}
void solve(){
    int n,m;
    std::cin>>n>>m;
    std::vector<std::vector<char>> s(n+1,std::vector<char>(m+1));
    std::vector<std::vector<int>> num(n+1,std::vector<int>(m+1));
    for (int i=1;i<=n;i++){
        for (int j=1;j<=m;j++){
            std::cin>>s[i][j];
            if (s[i][j]=='.'){
                num[i][j]=++cnt;
            }
        }
    }

    for (int i=1;i<=n;i++){
        for (int j=1;j<=m;j++){
            if (s[i][j]=='*') continue;
            if (i+1<=n&&s[i+1][j]=='.'){
                int x=num[i][j],y=num[i+1][j];
                a[x][x]++;
                a[y][y]++;
                a[x][y]--;
                a[y][x]--;
            }
            if (j+1<=m&&s[i][j+1]=='.'){
                int x=num[i][j],y=num[i][j+1];
                a[x][x]++;
                a[y][y]++;
                a[x][y]--;
                a[y][x]--;
            }
        }
    }
    for (int i=1;i<=cnt;i++){
        for (int j=1;j<=cnt;j++){
            a[i][j]=(a[i][j]+mod)%mod;
        }
    }
    cnt--;
    Gauss();
    ll ans=(flag?mod-1:1);
    for (int i=1;i<=cnt;i++){
        ans=ans*a[i][i]%mod;
    }
    std::cout<<ans<<"\n";
}

P3317 [SDOI2014] 重建
推式子,套板子,特殊处理概率为1的点,防止除0
矩阵树定理_第1张图片

#include 
#define ll long long
const int N=55;
double eps=0.000001;
int cnt,flag;
double a[N][N];
void Gauss(){
    for (int i=1;i<=cnt;i++){
        int pos;
        for (int j=i;j<=cnt;j++){
            if (fabs(a[j][i])>eps){
                pos=j;
                break;
            }
        }
        if (pos!=i) flag^=1;
        std::swap(a[pos],a[i]);
        for (int j=i+1;j<=cnt;j++){
            double use=a[j][i]/a[i][i];
            for (int k=1;k<=cnt;k++){
                a[j][k]-=a[i][k]*use;
            }
        }
    }
}
void solve(){
    int n;
    std::cin>>n;
    double ans=1;
    for (int i=1;i<=n;i++){
        for (int j=1;j<=n;j++){
            std::cin>>a[i][j];
            if (a[i][j]==1){
                a[i][j]=1-eps;
            }
            if (i<j){
                ans*=(1-a[i][j]);
            }
            a[i][j]=a[i][j]/(1-a[i][j]);
        }
    }
    for (int i=1;i<=n;i++){
        for (int j=1;j<=n;j++){
            if (i==j) continue;
            a[i][i]+=a[i][j];
            a[i][j]=-a[i][j];
        }
    }
    cnt=n-1;
    Gauss();
    for (int i=1;i<=cnt;i++){
        ans*=a[i][i];
    }
    if (flag) ans*=-1;
    std::cout<<std::fixed<<std::setprecision(8)<<ans;
}

你可能感兴趣的:(图论,算法)