首页 > 其他分享 >行列式、矩阵树定理

行列式、矩阵树定理

时间:2023-08-27 22:57:42浏览次数:47  
标签:matrix int 矩阵 定理 det 个数 行列式

推荐阅读: 矩阵树定理(+行列式) - command_block 的博客

行列式

定义

这个东西一般用于求解图的生成树个数(矩阵树定理)。

称一个大小为 \(n\times n\) 的矩阵 \(A\) 的行列式为 \(\det(A)\)(或 \(|A|\))。

\[\det(A)=\sum_{p\texttt{是一个大小为n排列}}(-1)^{F(p)}\prod_{i=1}^{n}A[i][p_i] \]

其中 \(F(p)\) 表示排列 \(p\) 的逆序对数。\(\prod_{i=1}^{n}A[i][p_i]\) 可以理解为每一行选一个,每次选的列不与其他行重复,将选中的数乘起来。

性质

  1. 单位矩阵 \(I\) 的行列式为 \(1\),上三角、下三角矩阵的行列式为对角线的值的乘积。
  2. 交换矩阵的任意两行,行列式变号。(交换一个排列的两个值,逆序对数的奇偶性一定发生变化)。
  3. 如果矩阵的某一行均为 \(0\) 则行列式为值为 \(0\)。(每行选且仅选一个)
  4. 若矩阵 \(A\) 的第 \(i\) 行和第 \(j\) 行相同,行列式的值为零。(交换第 \(i\)、\(j\) 行后行列式变号,但实际上矩阵 \(A\) 没有发生变化即行列式不变号,所以行列式值为零)。
  5. 若矩阵 \(A\) 的第 \(i\) 行乘以 \(k\),行列式的值也乘以 \(k\)。(每一行都会选一个)
  6. \( \det(\begin{matrix}a+e &b+f\\c &d\\\end{matrix})= \det(\begin{matrix}a &b\\c &d\\\end{matrix})+ \det(\begin{matrix}e &f\\c &d\\\end{matrix}) \)(结合行列式定义自行理解)
  7. \( \det(\begin{matrix}a+kc &b+kd\\c &d\\\end{matrix})=\det(\begin{matrix}a &b\\c &d\\\end{matrix}) \)(\(\det(\begin{matrix}kc &kd\\c &d\\\end{matrix})=\det(\begin{matrix}c &d\\c &d\\\end{matrix})=0\))

高斯消元求解行列式

根据性质 \(6\),我们可以用高斯消元求解行列式,将原矩阵转化为上三角矩阵、顺便记录交换行的次数的奇偶性即可。

至于如何用第 \(i\) 行将 \(a_{j,i}\) 变为零(\(n\ge j>i\))。可以用辗转相除法。

这样既可以求出任意一个矩阵在模 \(p\) 下的行列式的值了(\(p\) 可以不为质数)。

Code:

typedef long long LL;
#define arr2D vector<vector<int>>
#define arr vector<int>
int solve(arr2D &a,int n,int mo){
    bool Swap=false; LL ans=1;
    for(int i=1;i<=n;i++){
        int tmp=i;
        for(int j=i;j<=n;j++)if(a[j][i]){tmp=j;break;}
        if(!a[tmp][i]){return 0;}
        for(int j=tmp+1;j<=n;j++)if(a[j][i]&&a[j][i]<tmp)tmp=j;
        if(i!=tmp){a[i].swap(a[tmp]);Swap^=1;}
        for(int j=i+1;j<=n;j++){
            if(a[j][i]>a[i][i]){a[i].swap(a[j]),Swap^=1;}
            while(a[j][i]){
                int d=a[i][i]/a[j][i];
                for(int k=i;k<=n;k++)a[i][k]=(a[i][k]+(LL)(mo-d)*a[j][k]%mo)%mo;
                a[i].swap(a[j]),Swap^=1;
            }
        }
        ans=(LL)1LL*ans*a[i][i]%mo;
    }
    if(Swap)ans=(mo-ans)%mo;
    return ans;
}

矩阵树定理

定义

一般用于求解图的生成树问题。(若无特殊说明,默认图为无向图,图中一定没有自环

度数矩阵 \(D\) 满足 \(d_{i,i}=du_i\) 其他的 \(d_{i,j}=0,i\not=j\)(在无向图中 \(du_i\) 表示与 \(i\) 相连的边数),邻接矩阵 \(A\) 表示连边情况。其基尔霍夫矩阵为 \(K=D-A\)。则该图的生成树个数为矩阵 \(K\) 删去第 \(r\) 行、\(r\) 列后的矩阵的行列式的值。

如果矩阵树定理只能求生成树个数就太废了,实际上矩阵树定理求的所有生成树的边权乘积之和(求生成树个数时所有边权均为 \(1\))。

因此将度数矩阵 \(D\) 中的 \(d_{i,i}\) 改为与 \(i\) 相连的边权之和,邻接矩阵 \(A\) 中的 \(a_{i,j}\) 改为 \(i\) 和 \(j\) 之间的边权之和。其基尔霍夫矩阵为 \(K=D-A\)。则该图的所有生成树的边权乘积之和为矩阵 \(K\) 删去第 \(r\) 行、\(r\) 列后的矩阵的行列式的值。

因此在用矩阵树定理求解生成树个数时可以支持重边(边 \((i,j)\) 的权值为 \((i,j)\) 在原图中出现的次数)。

有向图的矩阵树定理

邻接矩阵 \(A\) 为有向图的连边情况。

度数矩阵要分类讨论:

  1. 度数矩阵 \(D\) 的 \(d_{i,i}=\sum{j=1}^{n}A_{i,j}\) 时求的是外向树生成树个数。
  2. 度数矩阵 \(D\) 的 \(d_{i,i}=\sum{j=1}^{n}A_{j,i}\) 时求的是内向树生成树个数。

若以 \(k\) 为根则其生成树个数为基尔霍夫矩阵删去第 \(k\) 行、\(k\) 列的矩阵的行列式的值。

例题

P4336 [SHOI2016] 黑暗前的幻想乡

直接容斥计算即可,\(\sum_{i}^{}(-1)^{n-P(i)}F(i)\),\(P(i)\) 表示集合 \(i\) 的元素个数,\(F(i)\) 表示包含集合 \(i\) 内的所有建筑商可以修建的边的图的生成树个数。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef vector<int> arr;
typedef vector<arr> arr2D;
constexpr int mo=1e9+7;

int sol(arr2D a,int n){
    LL ans=1;bool Swap=false;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            a[i][j]%=mo;
    for(int i=1;i<=n;i++){
        int tmp=i;
        for(int j=i;j<=n;j++)if(a[j][i]){tmp=j;break;}
        if(!a[tmp][i])return 0;
        for(int j=tmp+1;j<=n;j++)if(a[j][i]&&a[j][i]<a[tmp][i])tmp=j;
        if(i!=tmp)a[i].swap(a[tmp]),Swap^=1;
        for(int j=i+1;j<=n;j++){
            if(a[j][i]<a[i][i])a[i].swap(a[j]),Swap^=1;
            while(a[j][i]){
                int d=a[i][i]/a[j][i];
                for(int k=i;k<=n;k++)
                    a[i][k]=(a[i][k]+(LL)1LL*(mo-d)*a[j][k])%mo;
                a[i].swap(a[j]),Swap^=1;
            }
        }
        ans=(LL)ans*a[i][i]%mo;
    }
    if(Swap)ans=(mo-ans)%mo;
    return ans;
}

arr2D Val;
vector<pair<int,int>>e[55];
int n;

int Sol(int S){
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            Val[i][j]=0;
    int t=0;
    for(int i=0;i<n-1;i++)if((S>>i)&1){
        t++;
        for(auto j:e[i])
            Val[j.first][j.first]++,
            Val[j.second][j.second]++,
            Val[j.first][j.second]--,
            Val[j.second][j.first]--;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            Val[i][j]=(mo+Val[i][j]%mo)%mo;
    int ans=sol(Val,n-1);
    if((t&1)!=((n-1)&1))ans=(mo-ans)%mo;
    return ans;
}

int main(){
    cin>>n;
    Val.resize(n+1,arr(n+1));
    for(int i=0;i<n-1;i++){
        int m;cin>>m;e[i].resize(m);
        for(auto& j:e[i])
            cin>>j.first>>j.second;
    }
    int ans=0;
    for(int i=1;i<(1<<n-1);i++)
        (ans+=Sol(i))%=mo;
    cout<<ans<<'\n';
    return 0;
}

P3317 [SDOI2014] 重建

边权改为 \(\frac{p_{i,j}}{1-p_{i,j}}\),注意 \(p_{i,j}=1\) 的情况,手动加个误差即可。

#include <bits/stdc++.h>
using namespace std;
typedef vector<double> arr;
typedef vector<arr> arr2D;
constexpr double eps=1e-10;
double sol(arr2D &a,int n){
    double ans=1.;
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++)if(fabs(a[j][i])>eps){
            double d=a[j][i]/a[i][i];
            for(int k=i;k<=n;k++)a[j][k]-=d*a[i][k];
        }
        ans*=a[i][i];
    }
    return ans;
}
arr2D a;
int n;
int main(){
    cin>>n;
    a.resize(n+1,arr(n+1,0.));
    double pi=1.;
    for(int i=1;i<=n;i++){
        double sum=0;
        for(int j=1;j<=n;j++){
            cin>>a[i][j];a[i][j]+=eps;
            if(i<j)pi*=(1.-a[i][j]);
            if(i^j)a[i][j]=a[i][j]/(1.-a[i][j]);
            sum+=a[i][j],a[i][j]=-a[i][j];
        }
        a[i][i]=sum;
    }
    printf("%.6lf\n",sol(a,n-1)*pi);
    return 0;
}

标签:matrix,int,矩阵,定理,det,个数,行列式
From: https://www.cnblogs.com/dadidididi/p/17661040.html

相关文章

  • 数组章节的进阶54. 螺旋矩阵
    54. 螺旋矩阵1classSolution:2defspiralOrder(self,matrix:List[List[int]])->List[int]:3m,n=len(matrix),len(matrix[0])4res=[]#存放遍历后的结果5startx=starty=067foroffsetinrange(min(m,......
  • 剑指Offer 29. 顺时针打印矩阵
    题目链接:剑指Offer29.顺时针打印矩阵题目描述:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。解法思路:本题的题意比较简单,也就是螺旋打印矩阵,但是这里面有技巧,使用数组定义好在打印过程中的四个移动方向在遍历的过程中,每次都是在该方向上移动,当移动......
  • 关于欧几里得算法与裴蜀定理的证明
    前言:因为某次考试订正T4,用到了exCRT,然后发现我和lws不会exgcd……所以来把gcd到exgcd重新学一下,就写了这篇trick。欧几里得算法:求证:\[\gcd(a,b)=\begin{cases}\gcd(b,a\bmodb)&b\ne0\\a&b=0\\\end{cases}\]记:\(a=qb+r\),其中\(q=\lfloor\fracab\r......
  • 线性同余方程+中国剩余定理
    逆元求解\(ax=b\pmodm\),其实等价于\(ax+my=b\),然后扩欧就无了。可以应用于求当是\(a,p\)互质,求\(a\)在模\(p\)意义下的逆元,方法就是求解\(ax=1\pmodp\)。中国剩余定理(CRT)问题:有\(m_1,m_2,...,m_n\),\(n\)个整数两两互质,还给定\(a_1,a_2,...,a_n\),需要我们求解一个方程组:\(......
  • 稀疏矩阵的压缩存储及转置,快速转置法,C++代码实现
    /*稀疏矩阵的压缩存储及转置*/#include<iostream>usingnamespacestd;/*三元组顺序表的类型定义*/#defineitemSize100typedefstruct{introw,col;intitem;}thNode;typedefstruct{thNode*data;//data[0]不用intm,n,t;//分别表示行数、列......
  • 高等数学——微分中值定理
    微分中值定理罗尔定理费马引理\(f(x)\)在\(x_{0}\)\(U(x_{0})\)有定义,在\(x_{0}\)处可导,如\(f(x)\lef(x_{0})\),所有的\(x\inU(x_{0})\)。则\(f'(x_{0})=0\)。导数等于零的点为函数的驻点(或稳定点,临界点)。罗尔定理如果\(f(x)\)满足:在\([a,b]\)连续。在......
  • 文理分科(最大流最小割定理)
    传送门数据范围一眼网络流。考虑每个人文理只能选一个,考虑最小割。考虑源点\(S\)向\((i,j)\)连一条费用为\(art_{i,j}\)的边,\((i,j)\)向汇点\(T\)连一条费用为\(science_{i,j}\)的边。若割\(S\)与\((i,j)\)的边,则表示\((i,j)\)不选文,若割\((i,j_\)与\(T\)的边,则表示\((i,j)\)不......
  • 欧拉定理学习笔记
    欧拉定理:若\(gcd(a,m)=1\),则\(a^{\varphi(m)}\equiv1\pmod{m}\)证明:令\(r_1,r_2,···,r_{\varphi(m)}\)为模m下的一个简化剩余系,则\(ar_1,ar_2,···,ar_{\varphi(m)}\)也为模m下的一个简化剩余系,令\(f=r_1*r_2*···*r_{\varphi(m)}\),则有:\(f\equivar_1*ar_2*···*ar_{......
  • 【Matlab 教程】-02 Matlab 基本操作与矩阵输入
    1、Matlab2020a界面简介2、命令行窗口1、操作符+-*/^在命令行窗口,输入表达式并回车计算,结果会以ans作为默认变量名,也可以在工作区查看优先级:()>^>*/>+-点击查看操作符+-*/^代码>>2+1ans=3 2+12-12/32*32^32、练习注意l......
  • 玩转 PI 系列-看起来像服务器的 ARM 开发板矩阵-Firefly Cluster Server
    前言基于我个人的工作内容和兴趣,想要在家里搞一套服务器集群,用于容器/K8s等方案的测试验证。考虑过使用二手服务器,比如DellR730,还搞了一套配置清单,如下:DellR7303.5尺寸规格硬盘CPU:2686v4*2内存:16g*8存储:480Gintelssd系统盘+6tsas希捷*2个数据盘RAID卡:h73......