浅谈线性代数

学了线性代数的一些内容。简单做一个笔记。

前置内容

什么是线性代数?

线性代数是数学的一个分支,它的研究对象是向量,向量空间(或称线性空间),线性变换和有限维的线性方程组。向量空间是现代数学的一个重要课题。 线性代数的理论已被泛化为算子理论。由于科学研究中的非线性模型通常可以被近似为线性模型,使得线性代数被广泛地应用于自然科学和社会科学中,因而在各种代数分支中占居首要地位。(摘自百度

学完线性代数,就有了对空间抽象成代数的思想。但是不一定有这个能力

前置数学知识

向量:c\vec{c},在计算机领域表示一个一维数组,在几何领域,表示一个有方向的线段。

矩阵:形如 [abcd]\begin{bmatrix} a & b \\ c & d \end{bmatrix} 之类的东西,记高为 nn,宽为 mm,则这是一个 n×mn\times m 的矩阵,写出下标时先行后列,如 bb 的下标为 (1,2)(1,2)

笛卡尔坐标系:平面直角坐标系和斜坐标系的统称。平面直角坐标系不必多说,斜坐标系就是平面直角坐标系中 x,yx,y 轴不一定互相垂直。

其他有关线性代数的符号会在后文提到。如果出现了这里没提到的符号,可以转步我的另一篇文章,里面会提到一些,如果一些符号在那里出现过,这里不会过多赘述。

关于本文

  • 如果有误,洛谷私信。
  • 本文不带有任何网上找的例题,如果有需要自行查找。
  • 没有提到的变量范围请根据上下文情景判断,一般为有理数范围或正整数范围。
  • 线性代数是一门深奥的学科,本文仅为信息学竞赛需要的内容。
  • 大部分内容源自校内讲课和视频讲解

向量

向量的表示和定义

向量有着多重含义。在计算机上,向量可以视为一个一维数组,在数学上,则是坐标系上一个带有方向的线段,在物理里,它是一个有方向和大小的标量(物理里一般称“矢量”)

向量可以视为一个有向的线段,如果对于一个线段 ABAB,记它的方向为从端点 AA 到端点 BB,则线段就具有了它的方向和长度。向量的长度就是有向线段的长度,向量 a\vec{a} 的长度记为 a\left\vert \vec{a} \right\vert。向量的长度也称作向量的模

因为长度之间是有长短关系的,所以向量的模具备大小关系。但是注意向量本身不具备大小关系,因为方向是不能比较大小的。对向量之间进行“大于”或“小于”等比较没有意义。

长度为一个单位长度的向量称为单位向量,若存在一个向量 x\vec{x} 与向量 a\vec{a} 方向相同(同向),且 x=1\left\vert \vec{x} \right\vert=1,则称 x\vec{x}a\vec{a} 方向上的单位向量。

如果两个向量的模相同,但是方向相反,称为两个向量互为负向量。也可以成为互为相反向量;如果两个向量的模相同,方向也相同,则它们为相等向量,例如一对相等向量 a\vec{a}b\vec{b},可以记为 a=b\vec{a}=\vec{b}

如果一个向量的模为 00,则称这个向量为零向量。零向量的起点和重点重合,因此没有方向,或者也可以说其方向是任意方向。所有零向量都相等。

(如果正在讨论一个二维平面)向量一般认为是一个平面直角坐标系上的一个有向线段,且一般认为它的起始点都是原点。这时一个向量可以用它的终点的坐标 (x,y)(x,y) 表示,本文也会大量使用这种表示方法。此外向量还可以用一个 n×1n\times 1 的矩阵表示,例如 [abc]\begin{bmatrix} a \\ b \\ c \end{bmatrix}

如果两个向量或几个向量共线,则称这些向量是线性相关的,反之则为线性无关的。

向量的运算


向量的加法:两个向量 a,b\vec{a},\vec{b},它们相加记为 a+b\vec{a}+\vec{b},两个向量的和也是一个向量,在代数意义上,记两个向量为 (x1,y1),(x2,y2)(x_1,y_1),(x_2,y_2),则它们的和为 (x1+x2,y1+y2)(x_1+x_2,y_1+y_2)。在几何意义上,可以视为将一个向量的起点挂到另一个向量的终点上,它们的和的向量的起点是第一个向量的起点,终点是“挂起来的”那一个向量的终点。

向量加减

如图,u+v=w\vec{u}+\vec{v}=\vec{w},感性理解的表示为 (4,2)+(2,1)=(4+2,2+(1))=(6,1)(4,2)+(2,-1)=(4+2,2+(-1))=(6,1)


向量的减法:一个向量减去另一个向量,相当于一个向量加上另一个向量的负向量。在几何上,可以将两个向量的起点都放在原点,则两个向量的终点组成的就是差的向量,向量的方向是从作为减数向量的终点连向作为被减数的向量的终点,还是上面的图,wu=v\vec{w}-\vec{u}=\vec{v}


向量的数乘:数乘也叫叉积。一个向量乘上一个实数,实数和向量的乘积是一个向量。实数 aa 乘上向量 bb 记作 aba\vec{b}(这里不能写乘号)。一个数称一个向量,就是将向量内的每一个元素都乘上这个数。举例为:x[abc]=[axbxcx]x\begin{bmatrix} a \\ b \\ c \end{bmatrix}=\begin{bmatrix} ax \\ bx \\ c x \end{bmatrix}

在几何意义上,向量的数乘可以对这个向量的缩放(保持起点不变,缩放长度),即假设有 xax\vec{a},则意味着将 a\left\vert \vec{a} \right\vert 缩放 x\left\vert x \right\vertxx 的绝对值)倍。如果 x<0x<0,则意味着将 a\vec{a} 反向。

向量乘

如图,标记为绿色的向量 w\vec{w}(一部分与 u\vec{u} 重合)为 2u2\vec{u},标记为淡紫色的向量 v\vec{v}1.745u-1.745\vec{u}


向量的点积:也叫做数量积。一个向量乘上另一个向量,乘积是一个实数。记作 ab\vec{a}\cdot\vec{b}(只能用 \cdot 表示乘号,不能用 \ast×\times),设向量 a=[a1,a2,a3,,an]\vec{a}=[a_1,a_2,a_3,\cdots,a_n] ,向量 b=[b1,b2,b3,,bn]\vec{b}=[b_1,b_2,b_3,\cdots,b_n]。则

ab=i=1nai×bi\vec{a}\cdot\vec{b}=\sum_{i=1}^n a_i\times b_i

还是上面那个图,有 v=(6.98,3.49),w=(8,4)\vec{v}=(-6.98,-3.49),\vec{w}=(8,4),因此 vw=69.8\vec{v}\cdot\vec{w}=-69.8


向量的叉积:也叫向量积。一个向量乘上另一个向量,乘积是一个向量。记作 a×b\vec{a}\times\vec{b}(只能用 ×\times 表示乘号,不能用 \ast\cdot),设 a×b=c\vec{a}\times\vec{b}=\vec{c},将 a\vec{a}b\vec{b} 的夹角记为 θ(0θ180)\theta (0^\circ\leqslant\theta\leqslant 180^\circ),有 c=absinθ\left\vert \vec{c} \right\vert=\left\vert \vec{a} \right\vert \cdot \left\vert \vec{b} \right\vert \cdot\sin\theta。且 c\vec{c} 的方向垂直于 a,b\vec{a},\vec{b} 所决定的平面,且指向按 a\vec{a} 转向 b\vec{b} 的右手定则确定。

右手定则:

a×b\vec{a}\times\vec{b} 的方向:伸出右手,四指由 a\vec{a} 开始,指向 b\vec{b},拇指的指向就是 a×b\vec{a}\times\vec{b} 的方向,且垂直于 a\vec{a}b\vec{b} 所在的平面。


矩阵

向量向矩阵的过度

在平面直角坐标系上,有两个重要的向量,一个从原点出发,方向为 xx 轴正方向,模为 11,记为 ı^\widehat{\imath},另一个从也原点出发,方向为 yy 轴正方向,模为 11,记为 ȷ^\widehat{\jmath},当然如果是三维的,还有 zz 轴方向的 k^\widehat{k},等等。我们将 ı^\widehat{\imath}ȷ^\widehat{\jmath} 的坐标标记下来,就是 (0,1)(0,1)(1,0)(1,0)。这些特殊的向量称为基向量

现在做一个奇特的幻想:假设我改变了 ı^\widehat{\imath}ȷ^\widehat{\jmath} 这两个向量,使得整个 xx 轴和 yy 轴的方向(以及单位长度)都发生了改变,整个平面直角坐标系就会倾斜扭曲。但无论如何,总会保证 xx 方向或 yy 方向之间的单位长度等距,而且没有曲线。

这一过程实在比较抽象,需要视频理解。

ı^\widehat{\imath}ȷ^\widehat{\jmath} 的坐标放在一起,按 [xixjyiyj]\begin{bmatrix} x_i & x_j \\ y_i & y_j \end{bmatrix} 的方式排放,就变成了一个 2×22\times 2 的矩阵。此时 ı^\widehat{\imath}ȷ^\widehat{\jmath} 还没有任何改变,因此这个矩阵是 [1001]\begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}。如果你非常无聊把三维的空间的三个基向量放在一起,就是 [100010001]\begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} 的。如果你强大到可以想象出一个四维空间,并标出四个基向量放进去,就是这样: [1000010000100001]\begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} 的……

我们发现这几个矩阵都有一个共同的特点,它们的行数和列数相同(设为 nn),且只有 (1,1)(1,1)(n,n)(n,n) 这一条对角线上的数是 11,其他的数都是 00。这样的矩阵就是矩阵的单位元

至此,我们慢慢过度到了矩阵……

矩阵的表示和定义

矩阵的表示比较简单,形如 [abcd]\begin{bmatrix} a & b \\ c & d \end{bmatrix} 的东西都是矩阵……这在前置知识以及提及。矩阵的官方定义如下:

在数学中,矩阵(Matrix)是一个按照长方阵列排列的复数或实数集合 ,最早来自于方程组的系数及常数所构成的方阵。矩阵的运算是数值分析领域的重要问题。将矩阵分解为简单矩阵的组合可以在理论和实际应用上简化矩阵的运算。对一些应用广泛而形式特殊的矩阵,例如稀疏矩阵和准对角矩阵,有特定的快速运算算法。(摘自百度

矩阵的单位元已经在上文解释过。关于矩阵的常用定义还有如下一些:

n×nn\times n 的矩阵 AA 的从 (1,1)(1,1)(n,n)(n,n) 的这条对角线的元素之和被称为矩阵的迹,记为 tr(A)\operatorname{tr}(A),用公示表达为:tr(A)=i=1nai,i\operatorname{tr}(A)=\sum_{i=1}^n a_{i,i}

矩阵可以进行转置,转置即将矩阵的行和列交换,AA 矩阵的转置矩阵记为 ATA^T,举例如下:

[123456]T=[142536]{\begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{bmatrix}}^T=\begin{bmatrix} 1 & 4 \\ 2 & 5 \\ 3 & 6 \end{bmatrix}

如果一个矩阵的转置矩阵还是它自己,则称这个矩阵为对称矩阵

如果一个矩阵 AA 中,对于每一个元素 ai,ja_{i,j},如果 i>ji>j,都满足 ai,j=0a_{i,j}=0 的矩阵,称为上三角矩阵,如果 i<ji<j,都满足 ai,j=0a_{i,j}=0 的矩阵,称为下三角矩阵,这两种矩阵统称为三角矩阵

如果一个矩阵和另外一个矩阵相乘,可以得到单位元矩阵,则称它们互为对方的逆矩阵(矩阵乘法的操作在下文有提到)。

可以将矩阵的一整行或一整列“拆下来”,用一部分向量的定义简化表述。比如可以称一个矩阵的某两行是线性相关的。

矩阵的运算

矩阵的加法:两个矩阵 A,BA,B,可以进行加法当且仅当这两个矩阵的行数和列数都对应相等。两个矩阵的和为一个矩阵,两个矩阵相加,就是对应位置的数相加。

矩阵的减法:两个矩阵 A,BA,B,可以进行减法当且仅当这两个矩阵的行数和列数都对应相等。两个矩阵的差为一个矩阵,两个矩阵相减,就是对应位置得数相减。

矩阵之间的乘法:两个矩阵 A,BA,B,可以进行乘法当且仅当一个矩阵的行数和另一个矩阵的列数相等(设相等的这个数为 nn),两个矩阵的积为一个矩阵,积的矩阵中的各个元素计算方法如下:

ci,j=k=1nai,k×bk,jc_{i,j}=\sum_{k=1}^n a_{i,k}\times b_{k,j}

矩阵乘法满足结合律和分配率,不满足交换律。

矩阵和数的数乘:一个矩阵 AA 和另一个数相乘,乘积是一个矩阵。一个矩阵和一个数字相乘,就是矩阵的各个元素分别和这个数字相乘。

矩阵的初等变换:将一个矩阵的某一行(或某一列)记为 v\vec{v},则将原矩阵的另一行(或另一列,可以是原来那一行或原来那一列)加上 kvk\vec{v}kk 为非零实数),称为矩阵的初等变换。

值得注意的是:矩阵与矩阵的乘法不满足交换律,但是满足结合律和分配率。

矩阵的,矩阵的平方就是这个矩阵乘自己(因此要求这个矩阵行数和列数相等),矩阵的几次幂就是自乘几次,可以类比数的快速幂的算法求矩阵快速幂。

矩阵进阶

从排列到矩阵

想必大家对排列并不陌生。一个 1n1\sim n 的排列指的就是一个长度为 nn 的数列,其中 [1,n][1,n] 的数每一个数都恰好出现了一次,在后文中,我们记这个排列为 pp

而一个排列中的一对逆序对,指的是一对数 (i,j)(i,j),满足 i<ji<j,且在排列 pp 中有 pi>pjp_i>p_j。一个排列没有逆序对,当且仅当这个排列就是 1,2,3,,n1,2,3,\cdots,n 按小到大排好的顺序的排列。

定义一个排列的奇偶性就是这个排列的逆序对个数的奇偶性。考虑我们任意交换一个排列中相邻的两个数。只有这两个数要么本来是一对逆序对变成了不是逆序对,要么就是本来不是逆序对变成了是逆序对,而且这两个数改变对别的地方都没有影响(顶多就是本来和其中一个位置匹配逆序对变成了和另一个位置)。因此交换任意两个相邻的数,排列的奇偶性会变成和原来相反。

如果交换的不是相邻的两个数,而是任意位置的两个数呢?其实这就可以视为将左边那个数向右不停两两交换到目标位置。再将本来在目标位置的要换的数(因为已经换过了一次,所以是目标位置向左一格的位置)不断向左两两交换到它的目标位置即可。假设第一波交换换了 nn 次,那么第二波就换了 (n1)(n-1) 次。一共换了(2n1)(2n-1) 次,显然这个数是一个奇数。因此改变了奇数次奇偶性。最终得出:一个排列交换任意两个数,奇偶性都会改变

在排列奇偶性的定义之上,我们将奇偶性是奇的排列称为奇排列,反之则为偶排列

矩阵的行列式入门

对于一个 2×22\times 2 的矩阵 [abcd]\begin{bmatrix} a & b \\ c & d \end{bmatrix},它的行列式记为 abcd\begin{vmatrix} a & b \\ c & d \end{vmatrix},行列式是一个确切的数值,这个矩阵的行列式的值为 adbcad-bc

这只是因为 2×22\times 2 的矩阵比较常见,所以放了一个简单的公式,专门计算 2×22\times 2 的矩阵的行列式。如果你遇到的是另一个矩阵(不过行数和列数不相等的矩阵是没有行列式的),那就不能用这个小公式了。只能用一个奇妙的通式:

记矩阵行数和列数相等,都为 nn,设 pp 表示这个矩阵中选出 nn 个数,且不存在任意两个数在同一行或同一列的情况中,每一个选出的数所在的列数的排列。tt 表示排列的逆序对个数,pip_i 表示第 ii 行的选出的数的列数(即排列的第 ii 项)。有:

A=p(1)t×ai,pi\left\vert A \right\vert=\sum_p (-1)^t\times \prod a_{i,p_i}

显然直接算这个式子需要枚举每行选一个数的排列个数,一共有 n!n! 个不同的排列,因此总的复杂度是 Θ(n!)\Theta(n!) 的,后续会有优化计算行列式的方法。

矩阵行列式的性质

  • 一个矩阵的转置矩阵和原矩阵的行列式相同。(这也告诉我们每一行选一个数,算列数的排列,或者每一列选一个数算行数的排列,对行列式计算无影响)
  • 交换一个矩阵的两行,或者交换一个矩阵的两列,行列式的绝对值不变,符号取反。
  • 如果一个矩阵存在若干行(或若干列)是线性相关的,矩阵的行列式一定为 00
  • 将矩阵的某一行(或某一列)的数全部乘一个实数 kk,它的行列式对应地也会乘上 kk
  • 如果两个矩阵只有一行(或一列)不同,其他部分相同,记 sum1sum_1 为两个矩阵的行列式之和,sum2sum_2 为两个矩阵不同的这一行的所有元素相加,其他相同行元素不变的矩阵的行列式。一定有 sum1=sum2sum_1=sum_2
  • 对矩阵进行初等变换,矩阵的行列式不变。
  • 三角矩阵(包括上三角和下三角)的行列式是 (1,1)(1,1)(n,n)(n,n) 的对角线上所有元素之积。

高斯消元及升级运用

考虑一个形如下方的方程组:

{a1,1x1+a1,2x2+a1,3x3++a1,mxn=b1a2,1x1+a2,2x2+a2,3x3++a2,mxn=b2a3,1x1+a3,2x2+a3,3x3++a3,mxn=b3an,1x1+an,2x2+an,3x3++an,mxn=bn\begin{cases} a_{1,1}x_1 + a_{1,2}x_2 + a_{1,3}x_3 + \cdots + a_{1,m}x_n = b_1 \\ a_{2,1}x_1 + a_{2,2}x_2 + a_{2,3}x_3 + \cdots + a_{2,m}x_n = b_2 \\ a_{3,1}x_1 + a_{3,2}x_2 + a_{3,3}x_3 + \cdots + a_{3,m}x_n = b_3 \\ \vdots\quad\quad\quad\quad\quad\quad\quad\quad \ddots\quad\quad\quad\quad\quad\quad\quad\quad\quad \vdots \\ a_{n,1}x_1 + a_{n,2}x_2 + a_{n,3}x_3 + \cdots + a_{n,m}x_n = b_n \\ \end{cases}

不难发现这是一个 nn 元一次方程。高斯消元正是处理这里方程的好算法。

高斯消元算法

考虑将以上的方程组转化为一个系数矩阵:

[a1,1a1,2a1,mb1a2,1a2,2a2,mb2a3,1a3,2a3,mb3an,1an,2an,mbn]\left[\begin{array}{cccc|c} a_{1,1} & a_{1,2} & \cdots & a_{1,m} & b_1 \\ a_{2,1} & a_{2,2} & \cdots & a_{2,m} & b_2 \\ a_{3,1} & a_{3,2} & \cdots & a_{3,m} & b_3 \\ \vdots & & \ddots & & \vdots \\ a_{n,1} & a_{n,2} & \cdots & a_{n,m} & b_n \\ \end{array}\right]

因为是给计算机设计一个算法,不能加减消元和代入消元乱用。不妨怎么简单怎么来:先全部用加减消元,把它变成一个上三角矩阵,然后从最后一行开始一步一步回代。高斯消元和核心内容有三点:

  1. 两个方程交换位置,解不变。(矩阵交换两行)
  2. 一个方程整体乘一个数,解不变。(矩阵初等变换)
  3. 一个方程整体乘一个数,与另一个方程相加,解不变。(矩阵初等变换)

因此我们可以从矩阵的第一列开始,每次把这一列消成下面若干个都是 00 的形式即可。对于第一列可以这么操作:匹配第一行和其他所有行(假设目前在匹配第 kk 行),每次匹配将第一行乘上一个系数,使得 a1,1a_{1,1}ak,1a_{k,1} 互为相反数,然后将第 kk 行每个数都加上第一行对应数。

可是处理后面的列能不能还是用第一行来和其他行匹配呢?当然不行。因为此时其他行的第一列已经都是 00 了,第一行不是 00,这样子会让本来安排好为 00 的地方变为不是 00 了。实际上第一行本来就不需要所有数字都是 00,第一行随便什么数都可以。因此处理第二列时,可以从第二行开始向后匹配,处理第三列时,从第三行开始……

还有一个细节,如果求解的 xx 是有理数范围而不是整数范围,每次可以将当前这一列中最小的元素换上去,用这一行来匹配。

回代的时候,因为最后一行一定是 an,nxn=bna_{n,n}x_n=b_n 的形式,可以求出 xnx_n,回代到第 n1n-1 行,第 n1n-1 行是 an1,n1xn1+an1,nxn=bn1a_{n-1,n-1}x_{n-1}+a_{n-1,n}x_n=b_{n-1} 的形式,回代 xnx_n 后可以求出 xn1x_{n-1}……以此类推向上回代即可。

有时方程会出现无数解或无解。无解的情况就是出现某一行为 [0,0,0,0,,0k](k0)[0,0,0,0,\cdots,0 | k] (k\neq 0) 的情况。无数解就是出现 [0,0,0,0,0,0,,00][0,0,0,0,0,0,\cdots,0 | 0] 的情况。在消元/回代时注意即可。

高斯-约旦消元算法

高斯-约旦消元是对高斯消元的一种改进。相比于朴素的高斯消元,高斯-约旦消元算法的优势在于:

  • 可以保证有理数解的精度更高。
  • 代码比朴素的高斯消元简单,且和高斯消元同样易读。
  • 没有回代的过程,消元时可以直接将矩阵消为只有对角线为非 00 数其他地方都是 00 的情况。

高斯-约旦消元法步骤如下:

  1. 选择一个没有被选过的未知数和包含这个未知数(即系数不为 00)的方程。
  2. 将它的系数化为 11
  3. 匹配其他行,加减消元消去其他方程的这个未知数。
  4. 重复 1,2,31,2,3 步,直到每一个未知数都被选过一次,此时每一行只会有一项有系数。

以上步骤结束后,你将会得到这样一个方程组:

{k1x1=b1k2x2=b2k3x3=b3knxn=bn\begin{cases} k_1x_1 = b_1 \\ k_2x_2 = b_2 \\ k_3x_3 = b_3 \\ \vdots\quad \ddots\quad \vdots \\ k_nx_n = b_n \\ \end{cases}

显然不需要回代,方程组的解为 xi=bikix_i=\dfrac{b_i}{k_i},不难发现其基本思路和高斯消元差不太多,因为不需要回代码量少了些许。

高斯-约旦消元的参考代码如下:

for(register int j=1;j<=n;++j){ // 枚举列
    int k=j;
    for(register int i=j+1;i<=n;++i) // 这里找的是最大项,好直接判断是否有唯一解,见后面
        if(ab(a[i][j])-ab(a[k][j])>=eps) // double 类型直接判相等容易出锅
            k=i; // 找到系数最大的那一行
    if(!a[k][j]) // 最大的都消成 0 了,说明这里所有的系数都是 0 了
        return -1; // 不是唯一解的情况
    for(register int i=1;i<=n+1;++i) // 第 n+1 项是系数后面的常数(即前文的 b)
        swap(a[k][i],a[j][i]); // 为了方便操作,交换一下
        for(register int i=1;i<=n;++i){
            if(i==j) continue; // 消掉别的行
            double p=a[i][j]/a[j][j]; // 计算我要乘的系数
            for(register int k=j+1;k<=n+1;++k)
                a[i][k]-=a[j][k]*p; // 加减消元
		}
	}

高斯消元优化行列式计算

这里只需要简单提一下即可。前面我们提到一个矩阵进行初等变换,行列式是不会变的,如果交换两行,行列式的符号会取反。所以可以用高斯消元的方法,将原矩阵消成一个上三角矩阵,中途可以开一个变量 tottot 统计一下交换了几次行。

上三角矩阵的行列式就是对角线上的元素之积(前文有提过),因此只要求出这个上三角矩阵的行列式 (Θ(n)\Theta(n)),然后乘上一个 (1)tot(-1)^tot,就得出最后的答案了。

很明显瓶颈不在于求上三角矩阵的行列式,而是 O(n3)\operatorname{O}(n^3) 的高斯消元,整体复杂度是 O(n3)\operatorname{O}(n^3) 级别的,但至少已经比原来的朴素枚举排列的 Θ(n!)\Theta(n!) 好得多了。

余子式和代数余子式

对于一个矩阵 AA,定义其中一个元素 ai,ja_{i,j} 的代数余子式 Mi,j\operatorname{M}_{i,j},为去掉这个元素所在的一整行和所在的一整列,剩下的部分“拼成”一个矩阵后,这个矩阵的行列式。而这个元素的余子式(记为 Ai,j\operatorname{A}_{i,j})为 Ai,j=Mi,j×(1)i+j\operatorname{A}_{i,j}=\operatorname{M}_{i,j}\times(-1)^{i+j}

余子式和代数余子式的性质

对于某一个 n×mn\times m 的矩阵,存在某一行(设为第 ii 行)中,除了某一列 (设为第 jj 列)的数,这一行的其他数都是 00。(即存在一对数 (i,j)(i,j),满足 ai,j0a_{i,j}\neq 0,且 1km\forall 1\leqslant k\leqslant mkjk\neq j,有 ai,k=0a_{i,k}=0)整个矩阵可以用这个元素的余子式和这个数本身求出来。

我们考虑去掉第 ii 行第 jj 列,可以视为将这一行不断交换,换到第一行;将这一列不断交换,换到第一列,此时去掉新矩阵的第一行第一列,剩下的矩阵就不需要“分开”再“拼”了。此时总的积就是这个数的余子式乘上这个数本身。(即枚举排列是因为那一行都是 00 所以只能选它,对应的它所在列也选不了别的了)总的式子为 Ai,j×ai,j\operatorname{A}_{i,j}\times a_{i,j}

再考虑另一个问题,如果我只知道某一行(记为第 ii 行)的所有元素以及它们的余子式,怎么求出整个矩阵的行列式?

显然余子式就是我删掉这一行这一列剩下的矩阵的行列式(并乘上一个 1-1 的幂),不妨就视为这一行这一列我就选了 ai,ja_{i,j},因此和上面类似的做法,对于每一个元素,都对答案造成了 Ai,j×ai,j\operatorname{A}_{i,j}\times a_{i,j} 的贡献。因此一共的贡献(也就是整个矩阵的行列式)就是 jmAi,j×ai,j\sum_j^m\operatorname{A}_{i,j}\times a_{i,j}

另一个奇怪的性质是:如果存在某两行(记为第 ii 行和第 kk 行),满足 jmak,j×Ai,j\sum_j^m a_{k,j} \times \operatorname{A}_{i,j} 恰好等于整个矩阵的行列式,则原矩阵的行列式为 00

证明也比较抽象,考虑余子式的计算就是把它所在的行和列全部删掉,又因为整体的行列式不变,因此可以视为直接将第 kk 行的内容全部赋给第 ii 行,对行列式无影响。构造出的新矩阵满足有两行完全相同,也就是它们是线性相关的,因此行列式一定为 00

关于余子式的性质,可以上一个这样的例题,作为全文的结尾:

余子式例题及解析


例题:给出一个 n×nn\times n 的矩阵,并标记一个特殊行 kk,在线支持两种操作:修改特殊行的某一个数;查询整个矩阵的行列式。记操作次数为 mm,数据范围:1kn50,1m3×1071\leqslant k\leqslant n\leqslant 50,1\leqslant m\leqslant 3\times 10^7

考虑运用到矩阵的行列式和某一行所有数的值和余子式的关系。考虑到即使是同一行,修改一个数的值并不影响其他数的余子式。

因此不难想出一个算法:预处理特殊行所有数的余子式,维护 Ans=A(i,j)×ai,jAns=\sum \operatorname{A}_(i,j)\times a_{i,j},对于每次单点修改,假设修改 ax,ya_{x,y}bx,yb_{x,y},令 AnsAns 减去 Ax,y×ax,y\operatorname{A}_{x,y}\times a_{x,y},然后加上 Ax,y×bx,y\operatorname{A}_{x,y}\times b_{x,y} 即可。每次查询的时候直接输出 AnsAns

预处理部分复杂度为 O(n4)\operatorname{O}(n^4),每一次查询都是 O(1)\operatorname{O}(1) 的,非常巧妙地通过了。