数学(5)——线性代数:行列式
我也不知道为什么要学这个,不过线性代数相关的内容确实是计算机科学非常重要的知识。
关于行列式
前置芝士:矩阵和高斯消元。
行列式的定义
行列式 (
对一个
- 交换
1 行与1 列(进行一次矩阵转置),行列式不变;(2)
这两个性质的证明,可以考虑结合排列的奇偶性,然后直接重新带入公式观察。
-
行列式的行(列)所有元素等比例变化,则行列式也等比例变化;
(3) -
\begin{vmatrix} a_{1,1} &a_{1,2} &\cdots &a_{1,n}\\ a_{2,1} &a_{2,2} &\cdots &a_{2,n}\\ \vdots &\vdots &\ddots &\vdots\\ k\times a_{i,1} &k\times a_{i,2} &\cdots &k\times a_{i,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{n,1}&a_{n,2}&\cdots&a_{n,n}\\ \end{vmatrix} =k\times \begin{vmatrix} a_{1,1} &a_{1,2} &\cdots &a_{1,n}\\ a_{2,1} &a_{2,2} &\cdots &a_{2,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{i,1} &a_{i,2} &\cdots &a_{i,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{n,1}&a_{n,2}&\cdots&a_{n,n}\\ \end{vmatrix}
-
-
如果行列式对应矩阵
A 中有一行(列),是对应2 个矩阵B,C 中分别的2 行(列)所有元素之和。那么有\det(A)=\det(B)+\det(C) ;(4) -
\begin{vmatrix} a_{1,1} &a_{1,2} &\cdots &a_{1,n}\\ a_{2,1} &a_{2,2} &\cdots &a_{2,n}\\ \vdots &\vdots &\ddots &\vdots\\ b_{i,1}+c_{i,1} &b_{i,2}+c_{i,2} &\cdots &b_{i,n}+c_{i,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{n,1}&a_{n,2}&\cdots&a_{n,n}\\ \end{vmatrix} = \begin{vmatrix} a_{1,1} &a_{1,2} &\cdots &a_{1,n}\\ a_{2,1} &a_{2,2} &\cdots &a_{2,n}\\ \vdots &\vdots &\ddots &\vdots\\ b_{i,1} &b_{i,2} &\cdots &b_{i,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{n,1}&a_{n,2}&\cdots&a_{n,n}\\ \end{vmatrix} -
\begin{vmatrix} a{1,1} &a{1,2} &\cdots &a{1,n}\ a{2,1} &a{2,2} &\cdots &a{2,n}\ \vdots &\vdots &\ddots &\vdots\ c{i,1} &c{i,2} &\cdots &c{i,n}\ \vdots &\vdots &\ddots &\vdots\ a{n,1}&a{n,2}&\cdots&a{n,n}\ \end{vmatrix}
-
-
如果一个矩阵存在两行(列)成比例则
\det(A)=0 ;(5) -
\begin{vmatrix} a_{1,1} &a_{1,2} &\cdots &a_{1,n}\\ a_{2,1} &a_{2,2} &\cdots &a_{2,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{i,1} &a_{i,2} &\cdots &a_{i,n}\\ \vdots &\vdots &\ddots &\vdots\\ k\times a_{i,1} &k\times a_{i,2} &\cdots &k\times a_{i,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{n,1}&a_{n,2}&\cdots&a_{n,n}\\ \end{vmatrix}=0 -
这个证明可以直接代,但也有巧妙的办法:我们可以通过交换有比例关系的这两行得到新的矩阵
A' k\times a_{i,1}, k\times a_{i,2}, \cdots ,k\times a_{i,n} 现在就在上面的位置,一次交换使得\det(A)=-\det(A') ,我们通过(3) 将k 的比例变化放到行列式前,也就是A,A' 的k\times a_{i,1} k\times a_{i,2} \cdots k\times a_{i,n} 一行(列)变成a_{i,1},a_{i,2} \cdots ,a_{i,n} ,得到的新的矩阵分别是A'',A''' 我们发现A''=A''' 又有k\times |A''|=|A|=-k\times |A'''|=-|A'| ,一个数等于其相反数,所以有|A|=0 ;
-
-
把一个矩阵的一行(列)的值全部乘一个常数加到另一行(列)上,行列式值不变。
(6) -
\begin{vmatrix} a_{1,1} &a_{1,2} &\cdots &a_{1,n}\\ a_{2,1} &a_{2,2} &\cdots &a_{2,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{i,1} &a_{i,2} &\cdots &a_{i,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{j,1}+k\times a_{i,1} &a_{j,2}+k\times a_{i,2} &\cdots &a_{j,2}+k\times a_{i,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{n,1}&a_{n,2}&\cdots&a_{n,n}\\ \end{vmatrix} = \begin{vmatrix} a_{1,1} &a_{1,2} &\cdots &a_{1,n}\\ a_{2,1} &a_{2,2} &\cdots &a_{2,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{i,1} &a_{i,2} &\cdots &a_{i,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{j,1} &a_{j,2} &\cdots &a_{j,2}\\ \vdots &\vdots &\ddots &\vdots\\ a_{n,1}&a_{n,2}&\cdots&a_{n,n}\\ \end{vmatrix} -
证明也很简单,根据
(4) 有: -
\begin{vmatrix} a_{1,1} &a_{1,2} &\cdots &a_{1,n}\\ a_{2,1} &a_{2,2} &\cdots &a_{2,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{i,1} &a_{i,2} &\cdots &a_{i,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{j,1}+k\times a_{i,1} &a_{j,2}+k\times a_{i,2} &\cdots &a_{j,2}+k\times a_{i,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{n,1}&a_{n,2}&\cdots&a_{n,n}\\ \end{vmatrix} = \begin{vmatrix} a_{1,1} &a_{1,2} &\cdots &a_{1,n}\\ a_{2,1} &a_{2,2} &\cdots &a_{2,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{i,1} &a_{i,2} &\cdots &a_{i,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{j,1} &a_{j,2} &\cdots &a_{j,2}\\ \vdots &\vdots &\ddots &\vdots\\ a_{n,1}&a_{n,2}&\cdots&a_{n,n}\\ \end{vmatrix}+\begin{vmatrix} a_{1,1} &a_{1,2} &\cdots &a_{1,n}\\ a_{2,1} &a_{2,2} &\cdots &a_{2,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{i,1} &a_{i,2} &\cdots &a_{i,n}\\ \vdots &\vdots &\ddots &\vdots\\ k\times a_{i,1} &k\times a_{i,2} &\cdots &k\times a_{i,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{n,1}&a_{n,2}&\cdots&a_{n,n}\\ \end{vmatrix} -
\because (5) -
\therefore \begin{vmatrix} a_{1,1} &a_{1,2} &\cdots &a_{1,n}\\ a_{2,1} &a_{2,2} &\cdots &a_{2,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{i,1} &a_{i,2} &\cdots &a_{i,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{j,1}+k\times a_{i,1} &a_{j,2}+k\times a_{i,2} &\cdots &a_{j,2}+k\times a_{i,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{n,1}&a_{n,2}&\cdots&a_{n,n}\\ \end{vmatrix} = \begin{vmatrix} a_{1,1} &a_{1,2} &\cdots &a_{1,n}\\ a_{2,1} &a_{2,2} &\cdots &a_{2,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{i,1} &a_{i,2} &\cdots &a_{i,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{j,1} &a_{j,2} &\cdots &a_{j,2}\\ \vdots &\vdots &\ddots &\vdots\\ a_{n,1}&a_{n,2}&\cdots&a_{n,n}\\ \end{vmatrix}+0
-
行列式求值
那么知道了行列式这样一些性质,怎样在 OI 中高效地去求行列式呢。
直接根据定义计算,行列式求值是
有上面这些性质我们怎么该将问题简化呢。
消元
我们考虑一个情况,当一个矩阵任意一个位置出现
因为我们考虑公式中
我们利用上面的一些性质显然是可以让矩阵不断变化出现
显然瞎转化肯定是不行的,我们要让运算次数尽可能少。
如果学习了前置知识。我们知道求解线性方程组的算法高斯消元。其实高斯消元在做增广矩阵行(初等)变换为行最简形时的步骤和我们的转化有异曲同工之妙。
我们现在考虑将矩阵一行(列)消成只有最后一个元素非
对于
代数余子式求值
消元有什么用呢,我们引入线性代数中帮助我们求行列式的一个概念——代数余子式。
在一个
删除在
设
对于单一元素
我们有如下命题:
-
- $$ D=\sum_{j=1}^na_{i,j}\times A_{i,j},(i\in[1,n]) $$ - $$ D=\sum_{i=1}^na_{i,j}\times A_{i,j},(j\in[1,n]) $$ -
- $$ \sum_{k=1}^na_{i,k}\times A_{j,k}=0,(i,j\in[1,n],i\neq j) $$ - $$ \sum_{k=1}^na_{k,i}\times A_{k,j}=0,(i,j\in[1,n],i\neq j) $$
求值
我们回到刚才最后一行消元后的情况。运用第一个命题我们发现
也就是说如果对于这个
我们发现之前余子式
以此类推,如果我们一直:
-
对当前行列式消元
-
取最末(右下角)位的指和其余子式,余子式作为新行列式重新做;
按不同颜色一直递归下去做,我们发现最后我们得到
这样的一个下三角行列式。
我们就会发现这样一个矩阵的行列式是其对角线所有元素的乘积,也就是
这样就可以做了。
实现的时候有一些细节:
-
每次取新的余子式的时候需要注意奇偶性,及时变化正负,因为记得代数余子式是要乘上一个
(-1)^{(i_1+i_2+\dots+i_k)(j_1+j_2+\dots+j_k)} 的; -
如果题目要求
\bmod p 情况下的行列式,有些数是不一定有逆元的,或者说消元的时候精度出问题,我们可以考虑对两行(列)做辗转相减消元。-
\begin{bmatrix} 10 & a_{1,2} & a_{1,3} & \cdots &a_{1,n}\\ 4 & a_{2,2} & a_{2,3} & \cdots &a_{2,n}\\ a_{3,1} & a_{3,2} & a_{3,3} & \cdots &a_{3,n}\\ \vdots & \vdots & \vdots & \ddots & \vdots\\ a_{n,1} & a_{n,2} & a_{n,3} & \cdots &a_{n,n}\\ \end{bmatrix} -
对于这样的情况,求
a_{1,1}=a_{1,1}\bmod a_{2,1} -
有
\begin{bmatrix}2\\4\\\end{bmatrix} (我们现在只考虑这两个位置) -
swap(a[1][1],a[2][1]) -
有
\begin{bmatrix}4\\2\\\end{bmatrix} -
再做
a_{1,1}=a_{1,1}\bmod a_{2,1} -
有
\begin{bmatrix}0\\2\\\end{bmatrix} -
swap(a[1][1],a[2][1]) -
有
\begin{bmatrix}2\\0\\\end{bmatrix} -
这样分别解决了精度,逆元的一些问题。
-
消元操作是
代码
#include<bits/stdc++.h>
#define INL inline
#define ll long long
using namespace std;
const int N=605;
int n,a[N][N],MOD;
INL int read()
{
int x=0,w=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+ch-48,ch=getchar();
return x*w;
}
INL int sol()
{
int res=1,w=1;
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;++j)
{
while(a[i][i])
{
int div=a[j][i]/a[i][i];
for(int k=i;k<=n;++k)
{
a[j][k]=(a[j][k]-1ll*div*a[i][k]%MOD+MOD)%MOD;
}
swap(a[i],a[j]);w=-w;
}//对第 i 行和第 j 行做辗转相减。
swap(a[i],a[j]);w=-w;
}
}
for(int i=1;i<=n;i++)res=1ll*a[i][i]*res%MOD;
res=1ll*w*res;
return (res+MOD)%MOD;//经 典 模 加 模
}
int main()
{
n=read(),MOD=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a[i][j]=read();
int ans=sol();printf("%d\n",ans);
return 0;
}
Reference: