矩阵树定理

矩阵树定理

用于求解图上的生成树个数。生成树个数等于基尔霍夫矩阵任何一个 N − 1 N-1 N1阶主子式的行列式的绝对值。


基尔霍夫矩阵构造方法

A A A为邻接矩阵, D D D为度数矩阵,基尔霍夫(Kirchhoff)矩阵为 K = D − A K=D-A K=DA

a [ i ] [ i ] a[i][i] a[i][i]为点 i i i的度数, a [ i ] [ j ] a[i][j] a[i][j] i i i j j j之间边数的相反数。

例子:


行列式求法

高斯消元化为上三角矩阵,其行列式值为对角线的乘积。

若题目要求取模,高斯消元时使用辗转相除。(辗转相除代码参考例题)


变元矩阵树定理

求所有生成树总边积的和。行列式 a [ i ] [ i ] a[i][i] a[i][i]记录点 i i i边权和, a [ i ] [ j ] a[i][j] a[i][j]记录 i i i j j j之间边权的相反数。


例题

bzoj4031 HEOI2015 小Z的房间

矩阵树模板题。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll ;
const int mod = 1e9 ;
int tot ;
int s[15][15] ;
ll f[110][110] ;
int dx[4] = {0, 0, 1, -1}, dy[4] = {1, -1, 0, 0} ;
void add (int u, int v) {
    if (u > v) return ;
    f[u][u] ++; f[v][v] ++ ;
    f[u][v] --; f[v][u] -- ;
}
ll gauss () {
    ll ans = 1 ;
    for (int i = 1; i < tot; i ++) {
        for (int j = i + 1; j < tot; j ++) {
            while (f[j][i]) {
                ll t = f[i][i] / f[j][i] ;
                for (int k = i; k < tot; k ++)
                    f[i][k] = (f[i][k] - t * f[j][k] + mod) % mod ;
                swap (f[i], f[j]) ;
                ans = -ans ;
            }
        }
        ans = (ans * f[i][i]) % mod ;
    }
    return (ans + mod) % mod ;
}
int main() {
    int n, m ;
    cin >> n >> m ;
    for (int i = 1; i <= n; i ++) {
        char tmp[15] ;
        scanf("%s", tmp + 1) ;
        for (int j = 1; j <= m; j ++)
            if (tmp[j] == '.') s[i][j] = ++ tot ;
    }
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= m; j ++) {
            for (int d = 0; d < 4; d ++) {
                int x = i + dx[d], y = j + dy[d] ;
                if (s[i][j] && s[x][y]) add (s[i][j], s[x][y]) ;
            }
        }
    printf("%lld\n", gauss ()) ;
    return 0 ;
}

bzoj4596 SHOI2016 黑暗前的幻想乡

矩阵树定理 + 容斥

合法方案数 = 总方案数 - 一个未修建方案数 + 两个修建方案数…​

#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll ;
typedef pair<int, int> P ;
const int mod = 1e9 + 7 ;
inline int read () {
    int x = 0, f = 1; char c; c = getchar() ;
    while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar(); }
    while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar() ;
    return x * f ;
}
vector<P> road[20] ;
int n ;
ll f[20][20] ;
void add (int u, int v) {
    f[u][u] ++; f[v][v] ++ ;
    f[u][v] --; f[v][u] -- ;
}
ll gauss () {
    ll ans = 1 ;
    for (int i = 1; i < n; i ++) {
        for (int j = i + 1; j < n; j ++) {
            while (f[j][i]) {
                ll t = f[i][i] / f[j][i] ;
                for (int k = i; k < n; k ++)
                    f[i][k] = (f[i][k] - t * f[j][k] % mod + mod) % mod ;
                swap (f[i], f[j]) ;
                ans = -ans ;
            }
        }
        ans = ans * f[i][i] % mod ;
        if (ans == 0) return 0 ;
    }
    return (ans + mod) % mod ;
}
int main() {
    n = read () ;
    for (int i = 1; i < n; i ++) {
        int m = read () ;
        for (int j = 1; j <= m; j ++) {
            int u = read (), v = read () ;
            road[i].push_back (P (u, v)) ;
        }
    }
    ll ans = 0 ;
    for (int i = 1; i < (1 << (n - 1)); i ++) {
        memset (f, 0, sizeof f) ;
        int cnt = 0 ;
        for (int j = 1; j < n; j ++) {
            if (i & (1 << (j - 1))) {
                cnt ++ ;
                for (int k = 0; k < road[j].size(); k ++)
                    add (road[j][k].fi, road[j][k].se) ;
            }
        }
        if ((n - 1 - cnt) & 1) ans = (ans - gauss() + mod) % mod ;
        else ans = (ans + gauss ()) % mod ;
    }
    cout << ans << endl ;
    return 0 ;
}

bzoj3534 SDOI2014 重建

矩阵树可求 ∑ T ∏ e ∈ T p e \sum_T \prod_{e\in T}p_e TeTpe

此题求 ∑ T ( ∏ e ∈ T p e ∏ e ∉ T ( 1 − p e ) ) \sum_T (\prod_{e \in T}p_e \prod_{e \notin T}(1-p_e)) T(eTpee/T(1pe))

∑ T ( ∏ e ∈ T p e ∏ e ∉ T ( 1 − p e ) ) = ∑ T ( ∏ e ∈ T p e ∏ e ( 1 − p e ) ∏ e ∈ T ( 1 − p e ) ) = ∏ e ( 1 − p e ) ( ∑ T ∏ e ∈ T p e 1 − p e ) \sum_T (\prod_{e \in T}p_e \prod_{e \notin T}(1-p_e)) = \sum_T(\prod_{e \in T}p_e \frac{\prod_e(1-p_e)}{\prod_{e \in T}(1-p_e)})=\prod_e(1-p_e)(\sum_T \prod_{e \in T} \frac{p_e}{1-p_e}) T(eTpee/T(1pe))=T(eTpeeT(1pe)e(1pe))=e(1pe)(TeT1pepe)

令矩阵树中 p e = p e 1 − p e p_e = \frac {p_e} {1-p_e} pe=1pepe,求解。

#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-6 ;
int n ;
double a[55][55], ans = 1.0 ;
double gauss () {
    double res = 1 ;
    for (int i = 1; i < n; i ++) {
        int mx = i ;
        for (int j = i + 1; j < n; j ++)
            if (fabs (a[j][i]) > fabs (a[mx][i])) mx = j ;
        if (mx != i) swap (a[i], a[mx]) ;
        for (int j = i + 1; j < n; j ++) {
            double t = a[j][i] / a[i][i] ;
            for (int k = i; k < n; k ++)
                a[j][k] -= t * a[i][k] ;
        }
        res *= a[i][i] ;
    }
    return fabs (res) ;
}
int main() {
    cin >> n ;
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= n; j ++)
            scanf("%lf", &a[i][j]) ;
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= n; j ++) {
            double t = 1.0 - a[i][j] < eps ? eps : (1.0 - a[i][j]) ;
            if (i < j) ans *= t ;
            a[i][j] = a[i][j] / t ;
        }
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= n; j ++) {
            if (i != j) {
                a[i][i] += a[i][j]; a[i][j] = -a[i][j] ;
            }
        }
    printf("%.8lf\n", ans * gauss()) ;
    return 0 ;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值