矩阵树定理求图的生成树数量

矩阵树定理

在这里插入图片描述
在这里插入图片描述
M M T = L MM^T=L MMT=L

在这里插入图片描述
d e t ( L 0 ) det(L_0) det(L0)就是生成树的数量。

模板

SPOJ HIGH
题意
有重边无向图中,求生成树的数量

#include <bits/stdc++.h>
#define debug(__x) cout<<#__x<<"="<<__x<<endl
#define endl '\n'
using namespace std;
using ll = long long;
ll L[15][15];
ll det(ll a[][15],int n){
    ll ans = 1;
    for(int i = 1; i <= n; i++){		// 计算行列式从1开始
        for(int j = i + 1; j <= n; j++){
            while(a[j][i] != 0){
                ll u = a[i][i] / a[j][i];	// 逆元
                for(int k = i; k <= n; k++){
                    ll t = a[i][k] - a[j][k]*u;
                    a[i][k] = a[j][k];
                    a[j][k] = t;
                }
                ans = -ans;
            }
        }
        ans = ans*a[i][i];
    }
    return ans;
}
int main(){
    int T;
    cin.tie(0),cout.tie(0);
    ios::sync_with_stdio(false);
    cin>>T;
    while(T--){
        memset(L,0,sizeof(L));
        int n,m;
        cin>>n>>m;
        for(int v,u,i=0;i<m;++i){
            cin>>v>>u;
            --L[v][u];
            --L[u][v];
            ++L[v][v];
            ++L[u][u];
        }
        cout<<det(L,n-1)<<endl;
    }
}

应用

HDU多校Expectation
题意
在有重边的无向图中,求生成树的边权相与和的期望。

思路
a n d and and运算对于每个二进制位独立,可以分开求解。对于某一个二进制位 i i i,只有当生成树所有边权为1时该树的贡献为 2 i 2^i 2i,否则无贡献。问题就转化为了求边权都为1的边构成的图的生成树数量。求出所有权值和除以总的生成树数量就是答案了。

#include<bits/stdc++.h>
#define endl '\n'
#define debug(_x) cout<<#_x<<"="<<_x<<endl;
using namespace std;
using ll = long long;
const ll MOD = 998244353;
const int maxn = 2e5+5;
ll qpow(ll a,int b){
    ll ans=1;
    while(b){
        if(b&1) ans = ans * a % MOD;
        a = a*a % MOD;
        b >>= 1;
    }
    return ans;
}
ll det(ll a[][102],int n){  // 求行列式
    ll ans = 1;
    for(int i = 1; i <= n; i++){		// 计算行列式从1开始
        for(int j = i + 1; j <= n; j++){
            while(a[j][i] != 0){
                ll u = a[i][i] / a[j][i];	// 逆元
                for(int k = i; k <= n; k++){
                    ll t = ((a[i][k] - a[j][k]*u % MOD) + MOD)%MOD;
                    a[i][k] = a[j][k];
                    a[j][k] = t;
                }
                ans = -ans;
            }
        }
        ans = ans*a[i][i]%MOD;
    }
    return (ans+MOD)%MOD;
}
struct Edge{
    int u,v,w;
}e[10005];
int n,m;
ll L[102][102];
int main(){
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(false);
    int T;
    cin>>T;
    while(T--){
        cin>>n>>m;
        memset(L,0,sizeof(L));
        for(int i=0;i<m;++i){
            cin>>e[i].u>>e[i].v>>e[i].w;
            --L[e[i].u][e[i].v];
            --L[e[i].v][e[i].u];
            ++L[e[i].u][e[i].u];
            ++L[e[i].v][e[i].v];
        }
        ll up=0,down=det(L,n-1);
        for(int i=0;i<32;++i){
            int an = (1<<i);
            memset(L,0,sizeof(L));
            for(int j=0;j<m;++j){
                if(e[j].w&an){
                    --L[e[j].u][e[j].v];
                    --L[e[j].v][e[j].u];
                    ++L[e[j].u][e[j].u];
                    ++L[e[j].v][e[j].v];
                }
            }
            ll tmp = det(L,n-1);  // 去掉第n行第n列
            up+=an*tmp%MOD;
            up %= MOD;
        }
        debug(up);debug(down);
        cout<<up*qpow(down,MOD-2)%MOD<<endl;
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值