Matrix-Tree定理(sx之前填坑还来得及吗)

64 篇文章 0 订阅
19 篇文章 0 订阅

从入门到入土:矩阵树Matrix-Tree定理
参考blog

在正式介绍Matrix_Tree定理之前,我们需要一些前置知识

一些定义与定理

  • 对于一个无向图 G G ,ta的生成树个数等于其基尔霍夫Kirchhhoff矩阵任何一个N1阶主子式的行列式的绝对值

  • 所谓 N1 N − 1 阶主子式就是对于任意的一个r,将矩阵的第 r r 行和第r列同时删去得到的新矩阵

  • 基尔霍夫Kirchhoff矩阵的一种求法:
    基尔霍夫 Kirchhoff K= K i r c h h o f f   K = 度数矩阵 D D − 邻接矩阵 A A

基尔霍夫Kirchhoff矩阵的具体求法

    • 度数矩阵 D D :是一个NN的矩阵,其中
      D[i][j]=0(ij),D[i][i]=i D [ i ] [ j ] = 0 ( i ≠ j ) , D [ i ] [ i ] = i 号 点 的 度 数

    • 邻接矩阵 A A :是一个NN的矩阵,其中
      A[i][i]=0,A[i][j]=A[j][i]=i,j A [ i ] [ i ] = 0 , A [ i ] [ j ] = A [ j ] [ i ] = i , j 之 间 的 边 数

    • 基尔霍夫 Kirchhoff K= K i r c h h o f f   K = 度数矩阵 D D − 邻接矩阵 A A

    举个例子:
    这里写图片描述

    行列式det(K)求法

    得到基尔霍夫矩阵后,随便去掉某一行某一列并计算出新矩阵的行列式,其绝对值即为生成树个数

    这里写图片描述

    det(K)=p((1)τ(P)×K1,p1×K2,p2×K3,p3×...×KN,pN)

    其中 P P 1N的任意一个排列, τ(P) τ ( P ) 表示排列 P P 逆序对数
    而那个求和的每一项可以看做是在矩阵中选出N个数,这 N N 个数不同行不同列


    不大明白?
    看一个形象的表示方法:
    这里写图片描述
    在这个N=3的矩阵中,每一条线就代表着 K1,p1×K2,p2×...×KN,pN K 1 , p 1 × K 2 , p 2 × . . . × K N , p N

    那么 τ(P) τ ( P ) 又是什么呢?
    主要看两数连线的方向了,方向为 \ 的,表示连线的两个数之间无逆序关系,方向为 / 的,表示连线的两个数之间有逆序关系
    具体怎么计算呢?
    对于选中的一个序列,把每一行选中的数依次与ta的上一行,上上一行直至第一行选中的数连线
    若连线方向为 \ ,则无逆序关系,方向为 /,则累加一个逆序数
    第一行因为没有上一行,所以逆序数为0

    举个例子:
    这里写图片描述
    淡蓝色(青色,水蓝色,管你叫什么颜色)的线就是我们选中的排列
    a1,4 a 1 , 4 没有上一行,逆序数=0
    a2,1 a 2 , 1 a1,4 a 1 , 4 连线,逆序数=1
    a3,3 a 3 , 3 a1,4,a2,1 a 1 , 4 , a 2 , 1 连线,逆序数=1
    a4,2 a 4 , 2 a1,4,a2,1,a3,3 a 1 , 4 , a 2 , 1 , a 3 , 3 连线,逆序数=2

    τ(P)=1+1=2=4 τ ( P ) = 1 + 1 = 2 = 4


    求和式共有 N! N ! 项,暴力求解的复杂度为 O(N!)N O ( N ! ) ∗ N
    复杂度太高,我们看一下有没有优化解法

    行列式的性质

    性质一 . 互换矩阵的两行(列),行列式变号

    考虑对于原矩阵 K K ,我们可以得到其行列式的求和式:

    det(K)=p((1)τ(P)×K1,p1×K2,p2×K3,p3×...×KN,pN)

    若交换某两行的位置后得到了 K K ′ 矩阵,若写出其行列式的求和式,不难发现,如果不看符号位的变化,只看每一个乘积项,那么这两个的矩阵的行列式的求和式是完全相同的
    我们把相同的乘积项移到对应的位置,如图示:

    这里写图片描述
    然而显然,两个矩阵的这一项对应的排列 P P P不同:
    P :1  2  4  3 P   : 1     2     4     3
    P:3  2  4  1 P ′ : 3     2     4     1
    那么 τ(P)=1,(1)τ(P)=1 τ ( P ) = 1 , ( − 1 ) τ ( P ) = − 1
    τ(P)=4,(1)τ(P)=1 τ ( P ′ ) = 4 , ( − 1 ) τ ( P ′ ) = 1
    是不是都是这样呢?
    即原来是-1,现在是1;原来是1,现在是-1:交换任意逆序对变化量为奇数?
    答案是肯定的,证明如下:
    这里写图片描述

    由此可知,逆序对数的变化量为奇数,即两个det()求和式的对应的每一项的符号位都相反,所以互换矩阵的两行(列),行列式变号

    性质二 . 如果矩阵有两行(列)完全相同,则行列式为0

    证明:由性质1可知,交换这两行,得到的矩阵和原来相同,但是又要变化符号,则行列式的值只能为0

    性质三 . 如果矩阵的某一行(列)中的所有元素都乘以同一个数k,新行列式的值等于原行列式的值乘上数k

    证明:把求和式的每一项都提出一个公因子 k k

    推论:如果矩阵的某一行(列)中的所有元素都有一个公因子k,则可以把这个公因子k提到行列式求和式的外面

    性质四 . 如果矩阵有两行(列)成比例(比例系数k),则行列式的值为 0

    证明:把其中一行提出一个公因数k,那么剩下的det()求和式所代表的矩阵中存在一行或一列完全相同,则值为0

    性质五 . 如果把矩阵的某一行(列)加上另一行(列)的k倍,则行列式的值不变

    证明:可以从求和式子的每一项的那一行的那个元素下手,
    det()求和式拆成两个 det() d e t ( ) 求和式:
    det1() d e t 1 ( ) 与原矩阵的行列式求法相同
    det2() d e t 2 ( ) 所代表的矩阵中有两行成比例,比例系数为 k k ,值为0
    所以相比原来的行列式,值不变

    优化行列式的求法

    给出一个矩阵:
    这里写图片描述
    注意到这个是一个上三角矩阵
    其行列式的值为对角线的乘积(同理下三角矩阵)
    因为只有P=1 2 3 4时,乘积项中才没有0出现

    性质四可得,采用高斯消元的方法,把矩阵消为一个上三角矩阵后,然后求出对角线的积,便是该矩阵的行列式的值
    时间复杂度 O(N3) O ( N 3 )

    注意

    如果要求的矩阵不允许出现实数,且需要取模,则采用辗转相除的高斯消元法
    时间复杂度多一个 O(logN) O ( l o g N )


    讲完了?差不多了
    总结一下:
    求一个图的生成树个数,首先构造度数矩阵 D D 和邻接矩阵A

    K=DA 基 尔 霍 夫 矩 阵 K = D − A

    方便起见,我们去掉 K K 的第N行和第N列,得到一个新矩阵
    用高斯消元(取模情况下要使用辗转相除的高斯消元)得到新矩阵的上三角形
    对角线乘积的绝对值就是生成树个数


    什么是辗转相除的高斯消元

    我们再计算高斯消元的时候,如果模数是质数,那么除法逆元可以直接用费马小定理解决

    模数不是质数呢?
    我们求gcd的时候一个经典方法就是辗转相除法
    同样的,我们对于矩阵中的两行,不断地把主元较大的那一行减去主元较小的那一行,最终一定有一行主元为0,也就完成了消元

    但是减法的效率不够高,我们可以计算两个主元相除的商x
    一次性用主元较大的行直接减去主元较小的行乘上 x x
    复杂度O(logn)

    需要注意的是,我们在gauss的时候会交换行
    性质一可得:我们需要统计一下交换的次数
    如果次数为奇数,那么最后的答案还要乘上模意义下的 1 − 1

    要代码?没问题
    实际上就是在消元的时候变成了辗转相除

    ll gauss(int n) {
        int now,to;
        int opt=0;
        for (int i=1;i<=n;i++) {
            for (to=now;to<=n;to++)
                if (a[to][i]) break;
            if (to>n) return 0;                        //无解 
            if (to!=now) {
                for (int j=1;j<=n;j++)
                    swap(a[to][j],a[now][j]);
                opt^=1;
            }
            for (int j=i+1;j<=n;j++) 
                while (a[j][i]!=0) {
                    int x=a[j][i]/a[now][i];
                    if (x!=0) {
                        for (int k=1;k<=n;k++) {
                            a[j][k]-=(ll)x*a[now][k];
                            a[j][k]=(a[j][k]%p+p)%p;
                        }
                    }
                    else {
                        for (int k=1;k<=n;i++)        //系数比较小 
                            swap(a[now][k],a[j][k]);
                        opt^=1;
                    }
                }    
        }
        ll ans=1;
        for (int i=1;i<=n;i++) 
            ans=(ans*a[i][i])%p;
        if (opt) ans=mod-ans;
        return ans;
    } 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值