Latex 炸了

目录

线性代数

线性代数题单

本篇笔记中引用的资料均在题单简介中提及。

克服畏难情绪!

作为一名 OIer,学习线性代数的主要目的只有两个:解方程和计数。

意识到这一点,否则很容易迷失在抽象的森林中!

基础知识

向量

起猛了,直接做成了对《Linear Algebra Done Right》的翻译。

这部分大都是枯燥的,唯一有趣的地方应该是线性相关引理。

你只需要对向量空间的重要定义略知一二即可。

向量空间

对数域 \(F\),记 \(n\) 维向量空间 \(F^{n}\)

\[\begin{aligned} F^{n} = \{ (x_{1}, \dots, x_{n}) : x_{j} \in F \text{ for } j = 1, \dots, n \} \\ \end{aligned} \]

我们可以将向量 \((x_{1}, \dots, x_{n})\) 简写为 \(\vec{x}\)

特别地,可以将 零向量 \((0, \dots, 0)\) 简写为 \(0\)

向量空间 \(V\) 定义为带有加法和标量乘法且满足以下性质的集合:

commutativity

\[\begin{aligned} \vec{u} + \vec{v} = \vec{v} + \vec{u} \text{ for all } \vec{u}, \vec{v} \in V \\ \end{aligned} \]

associativity

\[\begin{aligned} (\vec{u} + \vec{v}) + \vec{w} = \vec{u} + (\vec{v} + \vec{w}) \text{ and } (ab)\vec{v} = a(b\vec{v}) \text{ for all } \vec{u}, \vec{v}, \vec{w} \in V \text{ and all } a, b \in F \\ \end{aligned} \]

additive identity

\[\begin{aligned} \text{there exists an element } 0 \in V \text{ such that } \vec{v} + 0 = \vec{v} \text{ for all } \vec{v} \in V \\ \end{aligned} \]

additive inverse

\[\begin{aligned} \text{for evrey } \vec{v} \in V, \text{there exists } \vec{w} \in V \text{ such that } \vec{v} + \vec{w} = 0 \\ \end{aligned} \]

multiplicative identity

\[\begin{aligned} 1\times \vec{v} = \vec{v} \text{ for all } \vec{v} \in V \\ \end{aligned} \]

distributive properties

\[\begin{aligned} a(\vec{u} + \vec{v}) = a\vec{u} + a\vec{v} \text{ and } (a + b)\vec{u} = a\vec{u} + b\vec{u} \text{ for all } a, b \in F \text{ and all } \vec{u}, \vec{v} \in V \\ \end{aligned} \]

子空间

\(V\) 的一个子空间 \(U\) 同样是一个子空间,并且与 \(V\) 共用一套加法和标量乘法。

判断 \(U\)\(V\) 的子空间需要检查 \(U\) 是否满足以下的所有性质:

additive identity

\[\begin{aligned} 0 \in U \\ \end{aligned} \]

closed under addition

\[\begin{aligned} u, v \in U \text{ implies } u + v \in U \\ \end{aligned} \]

closed under scalar multiplication

\[\begin{aligned} a \in F \text{ and } u \in U \text{ implies } au \in U \\ \end{aligned} \]

比如: \(\{ (x_1, x_{2}, 0) : x_{1}, x_{2} \in F \}\)\(F^{3}\) 的一个子空间。

比如:若 \(\{ (x_{1}, x_{2}, x_{3}, x_{4} \in F^{4} : x_{3} = 5x_{4} + b) \}\)\(F^{4}\) 的一个子空间,则一定有 \(b = 0\)

线性组合

现在有一个定义在数域 \(F\) 上的向量空间 \(V\)。取出 \(V\) 中的 \(m\) 个向量 \(\vec{v_{1}}, \dots, \vec{v_{m}}\),则:

\[\begin{aligned} \vec{b} = a_{1}\vec{v_{1}} + \dots + a_{m}\vec{v_{m}}, \text{ where } a_{1}, \dots, a_{m} \in F \\ \end{aligned} \]

\((\vec{v_{1}}, \dots, \vec{v_{m}})\) 的一个 线性组合

比如:\((7, 2, 9)\)\((2, 1, 3)\)\((1, 0, 1)\) 的一个线性组合,因为 \((7, 2, 9) = 2\times (2, 1, 3) + 3 \times (1, 0, 1)\)

张成(span)

\((\vec{v_{1}}, \dots, \vec{v_{m}})\) 的所有线性组合构成的向量空间被称作 \(V\)张成,即:

\[\begin{aligned} \mathrm{span}(\vec{v_{1}}, \dots, \vec{v_{m}}) = \{ a_{1}\vec{v_{1}} + \dots + a_{m}\vec{v_{m}} : a_{1}, \dots, a_{m} \in F \} \\ \end{aligned} \]

如果 \(\mathrm{span}(\vec{v_{1}}, \dots, \vec{v_{m}}) = V\),则称 \((\vec{v_{1}}, \dots, \vec{v_{m}})\) 张成 \(V\)

如果向量空间 \(V\) 内存在一组向量张成 \(V\),则称 \(V\) 是有限维度的。

比如:\((7, 2, 9) \in \mathrm{span}((2, 1, 3), (1, 0, 1))\)

直观理解:如果两个向量共线,那么它们的张成是一条直线。如果两条向量不共线,那么它们的张成是一个平面。如果三个向量不共线,那么它们的张成是整个三维空间。当两条向量不共线时,可以看作其中一条直线张成出一条直线,另一条向量移动这条直线,从而扫过整个二维空间;当三条向量不共线时,可以看作其中两条向量张成出一个平面,剩下一条向量移动这个平面,从而扫过整个三维空间。这也许是 “span” 的内核。

定理:向量空间 \(V\) 中任意一组向量的张成都是 \(V\) 的子空间。

定理:向量空间 \(V\) 中一组向量的张成是包含这组向量的最小子空间。

线性相关与线性无关

如果对于向量空间 \(V\) 中的一组向量 \((\vec{v_{1}}, \dots, \vec{v_{m}})\) 存在唯一的一组解 \(a_{1} = \dots = a_{m}\) 使得 \(a_{1}\vec{v_{1}} + \dots + a_{m}\vec{v_{m}} = 0\),则称 \((\vec{v_{1}}, \dots, \vec{v_{m}})\) 线性无关

比如:在 \(F^{4}\) 中,\(((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0))\) 是线性无关的。

如果一组向量不是线性无关的,那么它们就是线性相关的。换句话说,如果存在一组解 \(a_{1}, \dots, a_{m} \in F\) 并且不全为零,使得 \(a_{1}\vec{v_{1}} + \dots + a_{m}\vec{v_{m}} = 0\),则称 \((\vec{v_{1}}, \dots, \vec{v_{m}})\) 线性相关

比如:在 \(F^{3}\) 中,\(((2, 3, 1), (1, -1, 2), (7, 3, 8))\) 是线性相关的,因为 \(2(2, 3, 1) + 3(1, -1, 2) + (-1)(7, 3, 8) = (0, 0, 0)\)

零向量和任意向量线性相关

线性相关引理:如果 \(V\) 中的一组向量 \((\vec{v_{1}}, \dots, \vec{v_{m}})\) 线性相关且 \(\vec{v_{1}} \neq 0\),则存在 \(j \in \{ 2, \dots, m \}\) 使得:

  • \(\vec{v_{j}} \in \mathrm{span}(\vec{v_{1}}, \dots, \vec{v_{j - 1}})\)
  • 如果 \(\vec{v_{j}}\)\((\vec{v_{1}}, \dots, \vec{v_{m}})\) 中删除,则剩余向量的张成等于 \((\vec{v_{1}}, \dots, \vec{v_{m}})\) 的张成。

由于 \(\vec{v_{1}} \neq 0\),因此对于 \(a_{1}\vec{v_{1}} + \dots + a_{m}\vec{v_{m}} = 0\),必然存在 \(j \in \{ 2, \dots, m \}\) 使得 \(a_{j} \neq 0\)

取出使得 \(a_{j} \neq 0\) 的最大的 \(j\),则有 \(\vec{v_{j}} = -\frac{a_{1}}{a_{j}}\vec{v_{1}} - \dots - \frac{a_{j - 1}}{a_{j}}\vec{v_{j - 1}}\),于是第一点成立。

对于第二点,考虑取出 \(\mathrm{span}(\vec{v_{1}}, \dots, \vec{v_{m}})\) 中的元素 \(\vec{u} = c_{1}\vec{v_1} + \dots + c_{m}\vec{v_{m}}\),其中 \(\vec{v_{j}}\) 总可以替换为 \(-\frac{a_{1}}{a_{j}}\vec{v_{1}} - \dots - \frac{a_{j - 1}}{a_{j}}\vec{v_{j - 1}}\),因此删除 \(v_{j}\) 后剩余向量仍然可以线性组合出 \(\mathrm{span}(\vec{v_{1}}, \dots, \vec{v_{m}})\) 中的任意元素。所以第二点也成立。

定理:在有限维向量空间中,所有线性无关的向量组的大小不超过所有能张成 \(V\) 的向量组大小。

假设 \(V\) 中的一组向量 \(A = (\vec{u_1}, \dots, \vec{u_{m}})\) 线性无关,一组向量 \(B = (\vec{w_{1}}, \dots, \vec{w_{n}})\) 张成 \(V\),我们需要证明 \(m \le n\)

我们通过向 \(B\) 中不断加入 \(\vec{u}\)、删除 \(\vec{w}\) 说明这一点。

  • Step 1:由于 \((\vec{u_1}, \dots, \vec{u_{m}})\) 线性无关,所以其中的任意向量均不为 \(0\)。由于 \((\vec{w_{1}}, \dots, \vec{w_{n}})\) 张成 \(V\),所以 \((\vec{u_{1}}, \vec{w_{1}}, \dots, \vec{w_{n}})\) 线性相关,根据线性相关引理,我们可以删除任意 \(w_{j}(j \in \{ 1, \dots, n \})\),使得 \(\vec{u_{1}}\) 和剩余的 \(\vec{w}\) 仍然张成 \(V\)
  • Step j:在第 \(j - 1\) 步操作后的 \(B\) 仍然张成 \(V\),此时加入 \(\vec{u_{j}}\)\(\vec{u_{j}}\)\(B\) 必然线性相关。由于 \((\vec{u_{1}}, \dots, \vec{u_{j}})\) 线性无关,所以要删除 \(B\) 中的一个向量使得张成不变,则只能删除 \(\vec{w}\)
  • End\(m\) 次操作之后,\((\vec{u_1}, \dots, \vec{u_{m}})\) 全部加入到 \(B\) 中。如果中途 \(B\) 中的 \(\vec{w}\) 被删完了,即 \(m > n\),则说明 \((\vec{u_{1}}, \dots, \vec{u_{j}})\) 线性相关,这与前提条件相矛盾,所以只能有 \(m \le n\)

如果向量空间 \(V\) 中的一组向量线性无关并且张成 \(V\),则称这组向量为 \(V\) 的一组基。

形式化地,\((\vec{v_{1}}, \dots, \vec{v_{m}})\)\(V\) 的一组基当且仅当每个 \(\vec{u}\in V\) 都可以用唯一的一组 \(a_{1}, \dots, a_{m} \in F\) 表示为:

\[\begin{aligned} \vec{u} = a_{1}\vec{v_{1}} + \dots + a_{m}\vec{v_{m}} \\ \end{aligned} \]

比如:\(((1, 0, \dots, 0), (0, 1, 0, \dots, 0), (0, \dots, 0, 1))\)\(F^{n}\) 的一组基。特别地,我们称这种向量组为 \(F^{n}\)标准基

除了标准基之外,\(F^{n}\) 还可以有很多其它的基。

比如:\(((1, 2), (3, 5))\)\(F^{2}\) 的一组基。

定理:一个有限维向量空间的任意两个基长度相同。

假设 \(V\) 的两组基分别为 \(B_{1}\)\(B_{2}\)

考虑 \(B_{1}\) 是线性无关的,并且 \(B_{2}\) 张成 \(V\), 则 \(B_{1}\) 的大小不超过 \(B_{2}\)

再反向考虑一次,\(B_{2}\) 的大小不超过 \(B_{1}\)。所以 \(B_{1}, B_{2}\) 必然大小相等。

我们以 \(V\) 的基的大小定义 \(V\) 的维数,记作 \(\mathrm{dim}(V)\)

从线性变换到矩阵

这一节没有很多条条款款的知识汇总,但却是我认为理解线性代数最重要的部分。

线性变换是满足以下性质的 \(V\)\(W\)函数 \(T : V \to W\),记作 \(\mathcal{L}(V, W)\)

additivity

\[\begin{aligned} T(u + v) = T(u) + T(v) \text{ for all } u, v \in V \\ \end{aligned} \]

homogeneity

\[\begin{aligned} T(av) = aT(v) \text{ for all } a \in F \text{ and all } v \in V \\ \end{aligned} \]

几何意义:线性变换需要满足:

  • 直线仍然是直线。
  • 原点必须固定。

我们可以通过线性变换把一个向量变为另一个向量。

从线性方程组到矩阵

这一节没有很多条条款款的知识汇总,但却是我认为理解线性代数最重要的部分。

现在有一个方程组:

\[\begin{aligned} \begin{cases} 2x - y = 0 \\ -x + 2y = 3 \\ \end{cases} \end{aligned} \]

我们用下面这种形式来表示该线性方程组:

\[\begin{aligned} \left[ \begin{matrix} 2 & -1 \\ -1 & 2 \\ \end{matrix} \right] \left[ \begin{matrix} x \\ y \\ \end{matrix} \right] = \left[ \begin{matrix} 0 \\ 3 \\ \end{matrix} \right] \\ \end{aligned} \]

则一个线性方程组就可以表示为 \(Ax = b\) 这种简单的形式。

分别引入 行图像列图像 的概念。

行视角 来看,我们考察的就是每个完整的方程,这些方程在空间上对应了一些直线,而直线的交点就是方程组的解。

![Row Picture](D:\с++\Mathematic\线性代数\Row Picture.jpg)

列视角 来看,我们可以将所有方程的限制整体考虑,写成下面这种形式:

\[\begin{aligned} x\left[ \begin{matrix} 2 \\ -1 \\ \end{matrix} \right] + y\left[ \begin{matrix} -1 \\ 2 \\ \end{matrix} \right] = \left[ \begin{matrix} 0 \\ 3 \\ \end{matrix} \right] \\ \end{aligned} \]

这相当于找到 \(\left[ \begin{matrix} 2 \\ -1 \\ \end{matrix} \right]\)\(\left[ \begin{matrix} -1 \\ 2 \\ \end{matrix} \right]\) 的一个 线性组合 恰好等于 \(\left[ \begin{matrix} 0 \\ 3 \\ \end{matrix} \right]\)

![Col Picture](D:\с++\Mathematic\线性代数\Col Picture.jpg)

考虑线性方程组解的情况。假设线性方程组由 \(n\) 个方程组成,包含 \(m\) 个未知数。

从行视角来看:

  • 若存在两条直线平行而不重合,则无解。
  • 否则,若存在两条直线重合,则无穷解,否则有唯一解。

从列视角来看,方程组的解是对 \(m\) 个列向量的线性组合。在 \(F^{n}\) 空间内考虑列向量张成的空间 \(V\)

  • \(V = F^{n}\),则一定有解。如果列向量线性无关,则有唯一解,否则为无穷解。

  • 否则,可以想象在三维空间中一个二维平面为列向量张成的空间,\(b\) 为三维空间内的一个向量,若 \(b\) 不在这个平面上,则无论如何也不能组合出平面外的向量,故无解。

    若有解:如果列向量线性无关,则有唯一解,否则为无穷解。

接下来考察普遍情况的线性方程组的解的表示。

对于二元一次方程组:

\[\begin{aligned} \begin{cases} a_{1, 1}x_{1} + a_{1, 2}x_{2} = b_{1} \\ a_{2, 1}x_{1} + a_{2, 2}x_{2} = b_{2} \\ \end{cases} \end{aligned} \]

如果存在唯一解,则解为:

\[\Large{ \begin{aligned} \begin{cases} x_{1} = \frac{b_{1}a_{2, 2} - a_{1, 2}b_{2}}{a_{1, 1}a_{2, 2} - a_{1, 2}a_{2, 1}} \\ x_{2} = \frac{b_{2}a_{1, 1} - a_{2, 1}b_{1}}{a_{1, 1}a_{2, 2} - a_{1, 2}a_{2, 1}} \\ \end{cases} \end{aligned} } \]

这时还是可以接受的。

但对于三元一次方程组:

\[\begin{aligned} \begin{cases} a_{1, 1}x_{1} + a_{1, 2}x_{2} + a_{1, 3}x_{3} = b_{1} \\ a_{2, 1}x_{1} + a_{2, 2}x_{2} + a_{2, 3}x_{3} = b_{2} \\ a_{3, 1}x_{1} + a_{3, 2}x_{2} + a_{3, 3}x_{3} = b_{3} \\ \end{cases} \end{aligned} \]

如果存在唯一解,则解为:

\[\Large{ \begin{aligned} \begin{cases} x_{1} = \frac{b_{1}a_{2, 2}a_{3, 3} - b_{1}a_{3, 2}a_{2, 3} - b_{2}a_{1, 2}a_{3, 3} + b_{2}a_{3, 2}a_{1, 3} + b_{3}a_{1, 2}a_{2, 3} - b_{3}a_{2, 2}a_{1, 3}}{a_{1, 1}a_{2, 2}a_{3, 3} - a_{1, 1}a_{3, 2}a_{2, 3} - a_{2, 1}a_{1, 2}a_{3, 3} + a_{2, 1}a_{3, 2}a_{1, 3} + a_{3, 1}a_{1, 2}a_{2, 3} - a_{2, 1}a_{2, 2}a_{1, 3}} \\ x_{2} = \frac{a_{1, 1}b_{2}a_{3, 3} - a_{1, 1}b_{3}a_{2, 3} - a_{2, 1}b_{1}a_{3, 3} + a_{2, 1}b_{3}a_{1, 3} + a_{3, 1}b_{1}a_{2, 3} - a_{2, 1}b_{2}a_{1, 3}}{a_{1, 1}a_{2, 2}a_{3, 3} - a_{1, 1}a_{3, 2}a_{2, 3} - a_{2, 1}a_{1, 2}a_{3, 3} + a_{2, 1}a_{3, 2}a_{1, 3} + a_{3, 1}a_{1, 2}a_{2, 3} - a_{2, 1}a_{2, 2}a_{1, 3}} \\ x_{3} = \frac{a_{1, 1}a_{2, 2}b_{3} - a_{1, 1}a_{3, 2}b_{2} - a_{2, 1}a_{1, 2}b_{3} + a_{2, 1}a_{3, 2}b_{1} + a_{3, 1}a_{1, 2}b_{2} - a_{2, 1}a_{2, 2}b_{1}}{a_{1, 1}a_{2, 2}a_{3, 3} - a_{1, 1}a_{3, 2}a_{2, 3} - a_{2, 1}a_{1, 2}a_{3, 3} + a_{2, 1}a_{3, 2}a_{1, 3} + a_{3, 1}a_{1, 2}a_{2, 3} - a_{2, 1}a_{2, 2}a_{1, 3}} \\ \end{cases} \end{aligned} } \]

这太可怕了!

于是数学家们想到用一种更简洁的表示方式书写解,这个方式就是行列式。

在这个引入的例子中,行列式的英文名称更加耐人寻味——行列式(Determinant)可以 决定(Determin) 线性方程组的解。

矩阵

矩阵可以看作空间的线性变换

矩阵中的向量空间

现有一个 \(n \times m\) 的矩阵 \(A\)

列空间

矩阵 \(A\) 的所有列向量构成的向量空间称为 \(A\)列向量组,其张成的向量空间称为 \(A\)列空间,记作 \(C(A)\)

每个列向量有 \(n\) 个分量,因此列空间是 \(F^{n}\) 的子空间。

比如下面这个 \(4 \times 3\) 的矩阵:

\[\begin{aligned} A = \left[ \begin{matrix} 1 & 1 & 2 \\ 2 & 1 & 3 \\ 3 & 1 & 4 \\ 4 & 1 & 5 \\ \end{matrix} \right] \\ \end{aligned} \]

注意到第三列可以表示为第一列与第二列的线性组合,因此我们可以将第一列与第二列称为 主列

零空间

\(A\vec{x} = 0\) 的解 \(\vec{x}\) 构成的空间。

\(\vec{x}\) 每一维的值代表对 \(A\)\(m\) 个列向量进行线性组合的系数,因此 \(\vec{x}\)\(F^{m}\) 的子空间。

比如:

\[A\vec{x} = \left[ \begin{matrix} 1 & 1 & 2 \\ 2 & 1 & 3 \\ 3 & 1 & 4 \\ 4 & 1 & 5 \\ \end{matrix} \right] \left[ \begin{matrix} x_{1} \\ x_{2} \\ x_{3} \\ \end{matrix} \right] = 0 \\ \]

这里 \(\vec{x}\) 可以表示为 \(c\left[ \begin{matrix} 1 \\ 1 \\ -1 \\ \end{matrix} \right]\),可以看作三维空间中的一条直线,这代表 \(A\) 的零空间维数为 \(1\)

对于更一般的情况,当矩阵 \(A\) 的秩 \(\mathrm{rank}(A)\)\(r\) 时,有 \(r\) 个主列,其余的 \(m - r\) 列为 自由列。这 \(m - r\) 个自由列决定了 \(\vec{x}\) 中存在 \(m - r\) 个自由元,因此 \(A\) 的零空间维数为 \(m - r\)

矩阵的秩

对于 \(n \times m\) 矩阵 \(A\),列空间的维数称为 \(A\)​ 的 列秩

同时,可以考虑 \(A\)\(n\) 个行向量,类似地定义行空间,并称其维数为 \(A\)行秩

如果 \(\mathrm{rank}(A) = \min(n, m)\),则称 \(A\)满秩 的。

矩阵 \(A\) 的秩可以记作 \(\mathrm{rank}(A)\)

下面是一些关于矩阵的秩的事实:

  • 一个矩阵乘上一个可逆矩阵,其秩不变。
  • 行秩 = 列秩

矩阵的转置

矩阵 \(A = \left[ \begin{matrix} a_{1, 1} & a_{1, 2} & \cdots & a_{1, m} \\ a_{2, 1} & a_{2, 2} & \cdots & a_{2, m} \\ \vdots & \vdots & \ddots & \vdots \\ a_{n, 1} & a_{n, 2} & \cdots & a_{n ,m} \\ \end{matrix} \right]\) 的转置矩阵为 \(A^{T} = \left[ \begin{matrix} a_{1, 1} & a_{2, 1} & \cdots & a_{m, 1} \\ a_{1, 2} & a_{2, 2} & \cdots & a_{m, 2} \\ \vdots & \vdots & \ddots & \vdots \\ a_{1, n} & a_{2, n} & \cdots & a_{m, n} \\ \end{matrix} \right]\)

比如:\(\left[ \begin{matrix} 2 & 4 \\ 1 & 3 \\ \end{matrix} \right]\) 的转置矩阵为 \(\left[ \begin{matrix} 2 & 1 \\ 4 & 3 \\ \end{matrix} \right]\)

比如:\(\left[ \begin{matrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ \end{matrix} \right]\) 的转置矩阵为 \(\left[ \begin{matrix} 1 & 4 \\ 2 & 5 \\ 3 & 6 \\ \end{matrix} \right]\)

下面是若干转置的性质。暴力展开证明的过程是 naive 的,因此只给出感性理解的方式。

  • \(\colorbox{yellow}{\)(A{T}) = A\(}\)

    相当于转置两次后转回去了。

  • \(A, B\) 都是 \(n \times m\) 阶矩阵,则 \(\colorbox{yellow}{\)(A + B)^{T} = A^{T} + B^{T}\(}\)

    不管是加法后再转置,还是转置后再加法,都是对应位置在相加。

  • \(\colorbox{yellow}{\)(\lambda A)^{T} = \lambda A^{T}\(}\)

    \(\lambda\) 的效果是给每个位置的数值进行倍乘,不受转置影响,也不会影响转置。

  • \(\colorbox{yellow}{\)(AB)^{T} = B{T}A\(}\)

    转置前相乘,是 \(A\) 的对应行与 \(B\) 的对应列进行运算。那么转置后相乘,理应是 \(B^{T}\) 的对应行与 \(A^{T}\) 的对应列进行运算。

若干特殊矩阵

方阵

行列等长的矩阵。

单位矩阵

\[\left[ \begin{matrix} 1 & 0 & \cdots & 0 \\ 0 & 1 & \cdots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \cdots & 1 \\ \end{matrix} \right] \]

对角矩阵

\[\left[ \begin{matrix} a_{1} & 0 & \cdots & 0 \\ 0 & a_{2} & \cdots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \cdots & a_{n} \\ \end{matrix} \right] \]

标量矩阵

对角线元素相等的对角矩阵。

上三角矩阵 / 下三角矩阵

上三角矩阵:

\[\left[ \begin{matrix} a_{1, 1} & a_{1, 2} & \cdots & a_{1, n} \\ 0 & a_{2, 2} & \cdots & a_{2, n} \\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \cdots & a_{n, n} \\ \end{matrix} \right] \]

下三角矩阵:

\[\left[ \begin{matrix} a_{1, 1} & 0 & \cdots & 0 \\ a_{2, 1} & a_{2, 2} & \cdots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ a_{n, 1} & a_{n, 2} & \cdots & a_{n, n} \\ \end{matrix} \right] \]

对称矩阵

若一个方阵转置后不变,则称该方阵为对称矩阵。

初等行变换和初等行矩阵

倍加变换\(r_{1}^{'} = r_{1} + kr_{2}\)

\[\left[ \begin{matrix} 1 & \color{red}{k} & \cdots & 0 \\ 0 & 1 & \cdots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \cdots & 1 \\ \end{matrix} \right] \]

倍乘变换\(r_{1}^{'} = kr_{1}(k \neq 0)\)

\[\left[ \begin{matrix} \color{red}{k} & 0 & \cdots & 0 \\ 0 & 1 & \cdots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \cdots & 1 \\ \end{matrix} \right] \]

对换变换\(r_{1} \leftrightarrow r_{2}\)

\[\left[ \begin{matrix} \color{red}{0} & \color{red}{1} & \color{red}{\cdots} & \color{red}{0} \\ \color{red}{1} & \color{red}{0} & \color{red}{\cdots} & \color{red}{0} \\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \cdots & 1 \\ \end{matrix} \right] \]

在单位矩阵上应用其中一种变换一次得到的矩阵,称为 初等行矩阵

初等行矩阵乘上矩阵 \(A\),就相当于在矩阵 \(A\) 上实施了对应的初等行变换。

初等行变换和初等行矩阵是消元算法的基石。

高斯消元

高斯约旦消元法

先照搬 这篇博客 的内容。需要指出,这里仅针对于 \(n \times n\) 系数矩阵的消元。

  1. 选择一个尚未被选过的未知数作为主元,选择一个包含这个主元的方程。
  2. 将这个方程主元的系数化为1。
  3. 通过加减消元,消掉其它方程中的这个未知数。
  4. 重复以上步骤,直到把每一行都变成只有一项有系数。

下面是我的理解,并讨论 \(n \times m\) 系数矩阵(即 \(n\) 个方程,\(m\) 个未知数)的消元方法。

在 “从线性方程组到矩阵” 的引入中,我分析过解的存在情况。这里指出其中两个很重要的事实:

  • 方程组解的存在情况只与 列向量是否线性相关列空间 \(V\) 有关。

  • 方程组存在唯一解的必要条件是列向量线性无关,或者说:列满秩

我理解的高斯约旦消元的本质是:

通过重新建立一组原系数矩阵列空间 \(V\) 的基,使得在不改变解的存在性的情况下,让有解时方程组的解以极简形式表示出来

这句话虽然没有提及列向量的线性相关性,但我们知道同一个向量空间的基大小恒为其维数,因此不会对其产生影响。

而新的基组形如每个维度上至多只有一个向量上为非 \(0\) 元素。对于那些消元后不是主列的列向量,它们一定能被表示为主列的线性组合。

总结对于更一般的高斯约旦消元法,过程如下:

  1. 将方程组右侧的常数项与左侧系数矩阵合并为一个矩阵(增广矩阵),方便整体处理。但在理论分析时,若未特殊说明,均只考虑系数矩阵。

  2. 初始行指针 \(r = 1\)、列指针 \(c = 1\)

  3. 若当前列 \(r \sim n\) 行均为 \(0\),标记解不可能唯一,并跳过该列。

    否则,将该列上权值非 \(0\) 的一行调到第 \(r\) 行,并将该列所有除了第 \(r\) 行的值全部变成 \(0\)。为了方便倍加运算,可以考虑先把 \((r, c)\) 位置的元素化为 \(1\)。最后,行指针 \(r\) 向后移动一行。

  4. 列指针 \(c\) 向后移动一列。

  5. 当考虑完原系数矩阵的所有列向量后,可以开始判断解。

  6. 若行指针未遍历完每一行,则剩余的若干行向量必为零向量,若这些行向量对应的结果不为 \(0\),则判定无解。

    否则,若遍历到的列向量中有被跳过的,则为无穷解。

    否则,只需求解 \(m\) 个形如 \(kx = b\) 形式的方程,得到方程组的唯一解。

高斯约旦消元法有目的性地消成维度分明的基向量,使得算法过程更具 构造性的美。认识到这一点,我才可能明白什么是高斯消元——仅此而已。

// Sea, You & Me
const int N = 105;
const DB eps = 1e-15;
int n, m;
DB a[N][N];
void Jordan()
{
	int r = 1, c = 1;
	bool notsole = 0;
	for(; c <= m; ++c)
	{
		int p = r;
		for(int i = r; i <= n; ++i)
			if(fabs(a[i][c]) > eps)
			{
				p = i;
				break;
			}
		if(fabs(a[p][c]) < eps)
		{
			notsole = 1;
			continue;
		}
		for(int j = c; j <= m + 1; ++j)
			swap(a[r][j], a[p][j]);
		for(int j = m + 1; j >= c; --j)
			a[r][j] /= a[r][c];
		for(int i = 1; i <= n; ++i)
		{
			if(i == r) continue;
			for(int j = m + 1; j >= c; --j)
				a[i][j] -= a[i][c] * a[r][j];
		}
		++r;
	}
	if(r <= n) // check no solution
	{
		for(int i = r; i <= n; ++i)
			if(fabs(a[i][m + 1]) > eps)
			{
				puts("-1");
				return;
			}
	}
	if(notsole == 1) // check inf 
	{
		puts("0");
		return;
	}
	for(int i = 1; i <= m; ++i)
		a[i][m + 1] /= a[i][i];
	for(int i = 1; i <= m; ++i)
		printf("x%d=%.2lf\n", i, a[i][m + 1]);
}
int main()
{
	read(n); read(m);
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m + 1; ++j)
			cin >> a[i][j];
	Jordan();
	return 0;
}

带回代的高斯消元

先照搬 这篇博客 的内容。需要指出,这里仅针对于 \(n \times n\) 系数矩阵的阶梯型高斯消元。

  1. 由最左的非零列开始,这是一个主元列。主元位置在该列顶端。
  2. 在主元列中选取一个非零元作为主元。若有必要的话,对换两行使这个元素移到主元位置上。
  3. 用倍加行变换将主元下面的元素变成0.
  4. 暂时不管包含主元位置的行以及它上面的各行,对剩下的子矩阵使用上述的三个步骤直到没有非零行需要处理为止。
  5. 由最右面的主元开始,把每个主元上方的各元素变成0.若某个主元不是1,用倍乘变换将它变成1.

很长一段时间以来我都犯了一个错误:将高斯消元分为两类,一类是消成阶梯型的;一类是约旦消元法。

高斯消元从来都只有一个方法,那就是高斯消元它自己

高斯消元的回代求法是对线性方程组求解功能的进一步补全——你当然也可以在约旦消元法的基础上实现这一功能。

考虑这样一个特殊情况:当高斯消元多解时,我想要求出其中的一组解,如何求?

注意一个易错点(至少这困扰了我很久,并成为阻碍我理解高斯消元最大的绊脚石):非主列列向量不一定是零向量

这就意味着,我需要手动确定自由元的值,然后再回代到非零行向量对应的方程中去解出主元。

其实这就已经结束了。

但我们仍然可以提一嘴阶梯型高斯消元。它的实现自然是不如约旦法的,并且笔者目前来看,它能做的,约旦法都能做。

但我认为它确实有它存在的意义。从应用层面看,以它为基础可以得到求解行列式的算法。更重要的是,思考它为什么可以只将主元之下的元素全部消成 \(0\)(上阶梯矩阵型),能够更加明白判断解是否唯一的本质,而不是在形式化的产物中迷失掉那些更重要的东西。从最初学习阶梯型高斯消元,到学习约旦消元,然后偶尔接触到一两道消元的题目,却不解其中真意,不懂消元结果与方程组解集的联系。后来从高斯约旦消元入手,思考本质,再逐步完善算法细节,我想我应该学会了它吧。我称赞它,也贬低它,但最后与它和解,它已经独属于我——我将深爱着它。

下面是阶梯型高斯消元的带注释代码实现(\(n \times n\))。一百年前写的,十分丑陋请见谅。

#include<bits/stdc++.h>
using namespace std;
const int N=105;
const double eps=1e-10;
int n;
double a[N][N];
int GAUSS()
{
	int c=1,r=1;//column and row
	for(c=1;c<=n;c++)
	{
		int t=r;
		for(int i=r;i<=n;i++)
			if(fabs(a[i][c])>fabs(a[t][c]))
				t=i;//找出该列元素的绝对值最大值 
		
		if(fabs(a[t][c])<eps)
			continue;//该列全都被消为0  不必要且不能继续处理 
		
		for(int i=c;i<=n+1;i++)
			swap(a[r][i],a[t][i]);
		//把该列元素绝对值最大的一行与即将处理的这一行进行交换
		//purpose:1.优先除掉大数 尽可能不让其它数乘上大数 以损失精度
		//purpose:2.避免第c行第r行的元素为0 否则该次操作无意义 然而r++ 会导致答案错误 
		
		for(int i=n+1;i>=c;i--)//消1 
			a[r][i]/=a[r][c];//把该行关键元素x消成1的同时,其它元素也要/x
		
		for(int i=r+1;i<=n;i++)//消0(处理当前列该行下面的数)
			if(fabs(a[i][c])>eps)//非0才操作 
				for(int j=n+1;j>=c;j--)//当前列下的元素化为0 则对应行内的数值也要随之改变
					a[i][j]-=a[i][c]*a[r][j];//凡是循环中涉及到c的值都要逆序处理 
		r++;//处理下一行	
	}
	if(r<=n)//把某些元消掉了 
	{
		for(int i=r;i<=n;i++)
			if(fabs(a[i][n+1])>eps)//无解 
				return 2;
		return 1;//无穷组解 
	}
	for(int i=n;i>=1;i--)//从后往前推 
		for(int j=i+1;j<=n;j++)
			a[i][n+1]-=a[j][n+1]*a[i][j];//a[i][n+1]即为xi的解 
	return 0;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n+1;j++)
			scanf("%lf",&a[i][j]);
	int t=GAUSS();
	if(t==1)
		puts("Infinite group solutions");
	else if(t==2)
		puts("No solution");
	else
	{
		for(int i=1;i<=n;i++)
			printf("%.2lf\n",a[i][n+1]);
	}
	return 0;
}

tips

  • 注意对行处理时,要从后往前运算(除非把 \(a_{i, c}\) 事先存下来)。

最后指出:高斯消元可以看作是原矩阵乘上若干初等行矩阵

逆矩阵

\[\begin{aligned} \colorbox{yellow}{$I = AA^{-1} = A^{-1}A$} \\ \end{aligned} \]

其中 \(A^{-1}\) 即为 \(A\) 的逆矩阵。

由定义可知,只有方阵可能存在逆矩阵。

矩阵 \(A\) 可逆的充要条件是 \(A\) 是满秩矩阵。

从线性变换的角度讲,相当于对空间做了一个 \(A\) 变换后,又通过 \(A^{-1}\) 变换回去;亦可以将空间先做一个 \(A^{-1}\) 变换,又通过 \(A\) 变换回去。如果 \(A\) 不是满秩的,则它将会减小原本空间的维度,你无法从直线反推平面,也无法从平面反推整个三维空间,因此这种情况下的 \(A\) 不存在逆矩阵。

下面是关于逆矩阵的若干性质:

求解逆矩阵

在求解普通方阵的逆之前,先指出 初等矩阵是逆矩阵。一个显然的事情是,单位矩阵是满秩的。对单位矩阵做一次初等变换后,初等矩阵的列空间仍然可以看作是原来单位矩阵的列向量做线性组合张成的空间,维度不会发生变化,则初等矩阵一定是满秩的,也即是一定可逆的。

定理:方阵 \(A\) 可逆的充要条件是 \(A\) 为初等矩阵之积。

证明可以考虑由可逆矩阵的乘积仍然是可逆矩阵的结论,正反推一下即可。

考虑高斯消元的过程是对原矩阵不断做初等变换:

\[\begin{aligned} E_{k} \dots E_{2}E_{1}A = I \end{aligned} \]

如果我们将这些初等变换顺次相乘,就可以得到 \(A^{-1}\),即:

\[\begin{aligned} A^{-1} = E_{k} \dots E_{2}E_{1} \\ \end{aligned} \]

现在我对这个等式稍加修饰:

\[\begin{aligned} A^{-1}I = E_{k} \dots E_{2}E_{1}I \\ \end{aligned} \]

观察 \(E_{k} \dots E_{2}E_{1}A\)\(E_{k} \dots E_{2}E_{1}I\),我们可以将 \(A\)\(I\) 结合成一个矩阵同时做初等变换:

\[\begin{aligned} (A|I) \xrightarrow{E_{1}, E_{2}, \dots, E_{k}} (I | A^{-1}) \\ \end{aligned} \]

这样就可以更加简易地实现矩阵求逆了。

tips:下面给出了模(质数)意义下的高斯消元。

// Sea, You & Me
const int N = 405;
int n, a[N][N << 1];
void Jordan()
{
	int r = 1, c = 1;
	for(; c <= n; ++c)
	{
		int p = r;
		for(int i = r; i <= n; ++i)
			if(a[i][c] != 0)
			{
				p = i;
				break;
			}
		if(a[p][c] == 0)
		{
			puts("No Solution");
			return;	
		} 
		for(int i = c; i <= 2 * n; ++i)
			swap(a[r][i], a[p][i]);
		int inv = qwqmi(a[r][c]);
		for(int i = 2 * n; i >= c; --i)
			Mul(a[r][i], inv);
		for(int i = 1; i <= n; ++i)
		{
			if(i == r) continue;
			for(int j = 2 * n; j >= c; --j)
				Dec(a[i][j], mul(a[i][c], a[r][j]));
		}
		++r;
	}
	for(int i = 1; i <= n; ++i)
	{
		for(int j = 1; j <= n; ++j)
			printf("%d ", a[i][j + n]);
		puts("");
	}
}
int main()
{
	read(n);
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= n; ++j)
			read(a[i][j]);
	for(int i = 1; i <= n; ++i)
		a[i][i + n] = 1;
	Jordan();
	return 0;
}

行列式(Determinant)

行列式是对于方阵独有的性质,记作:

\[\begin{aligned} \det(A) = |A| = \left| \begin{matrix} 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_{n, 1} & a_{n, 2} & \cdots & a_{n ,n} \\ \end{matrix} \right| \end{aligned} \]

对行列式的若干思考

为什么行列式是方阵独有的性质:从线性变换的角度来看,矩阵是对空间的变换。考察一个向量空间 \(V = F^{m}\),被 \(n \times m\) 的矩阵 \(B\) 做一次变换,那么得到的结果是一个 \(n \times 1\) 的矩阵,这看起来与矩阵 \(B\) 的列向量属于同一类事物。那么先前的向量空间变成什么了,升维或者降维?一个 \(m\) 维的向量变成了 \(n\) 维的向量,这在几何上有什么含义?也许有,但这不是我所知晓的,也不是 OI 所需要的(也许我们能解释的是 \(\vec{x}\)\(B\)\(m\) 个列向量做线性组合)。

但对其施加一个 \(m \times m\) 的方阵 \(A\) 呢?一个 \(m\) 维的向量变成了一个 \(m\) 维的向量,这是我能够理解的。不妨具体地写出这个变换过程:

\[\begin{aligned} \text{ for all } \vec{x} \in V, \vec{x} \text{ turns to }: \\ \left[ \begin{matrix} a_{1, 1} & a_{1, 2} & \cdots & a_{1, m} \\ a_{2, 1} & a_{2, 2} & \cdots & a_{2, m} \\ \vdots & \vdots & \ddots & \vdots \\ a_{m, 1} & a_{m, 2} & \cdots & a_{m, m} \\ \end{matrix} \right] \left[ \begin{matrix} x_{1} \\ x_{2} \\ \vdots \\ x_{m} \\ \end{matrix} \right] \\ \text{and } V \to W \\ \end{aligned} \]

这里记 \(\vec{a_{i}} = \left[ \begin{matrix} a_{i, 1} \\ a_{i, 2} \\ \vdots \\ a_{i, m} \\ \end{matrix} \right]\),则 \(\vec{x}\) 变成了 \(x_{1}\vec{a_{1}} + \dots + x_{m}\vec{a_{m}}\),这是在对 \((\vec{a_{1}}, \dots, \vec{a_{m}})\) 做线性组合。貌似此时就应该运用主元对换的思想,\(\vec{x}\) 变换后的结果不应该从 \(\vec{x}\) 考虑,而是将 \(\vec{x}\) 分解后作为 \(\vec{a_{i}}\) 的系数,从 \(A\)\(m\) 个列向量进行考虑。如果 \(A\) 是满秩的,那么 \(W\) 仍然是 \(m\) 维的,这无可厚非。但如果 \(\mathrm{rank}(A) < m\),则 \(\mathrm{rank}(A)\) 个主列作为 \(W\) 的一组基(主列向量线性无关,且能够表示出其余 \(m - \mathrm{rank}(A)\) 列向量,故张成为 \(W\)。或者参见线性相关引理),决定了 \(W\) 的维数为 \(\mathrm{rank}(W)\)。这说明,\(W\)\(m\) 维上的产物,但是 \(W\) 本身从 \(V\) 降维了

在 ”从线性变换到矩阵“ 的引入中,我们知道行列式在几何上的直观理解是,二维平面上的有向面积,三维空间上的有向体积。在非满秩矩阵 \(A\) 的作用下,\(V \to W\) ”惨遭降维打击“,从平面降为直线,则直线不再具有面积;从三维空间降为平面,则平面不再具有体积。因此,这里想要指出的是,不可逆矩阵的行列式为 \(0\)


行列式的排列公式

行列式有一个 \(O(n!)\) 的直接计算式(莱布尼茨展开式):

\[\begin{aligned} \colorbox{yellow}{$\det(A) = \sum\limits_{{\text{permutation } p}}(-1)^{\mathrm{sgn}(p)}\prod\limits_{i = 1}^{n}a_{i, p_{i}}$} \\ \end{aligned} \]

其中 \(\mathrm{sgn}(p)\) 表示排列 \(p\) 的逆序对数。这个计算式在 ”从线性方程组到矩阵“ 的引入中是非常直观的。


行列式的性质

  • 对二阶行列式的简单求法:

    对于矩阵 \(A = \left[ \begin{matrix} a & b \\ c & d \\ \end{matrix} \right]\),它的行列式为 \(\colorbox{yellow}{\)ad - bc\(}\)

    思考一下矩阵在线性变换上的意义,当 \(b, c\)\(0\) 时,相当于只把一个基向量变为原来的 \(a\) 倍,把另一个基向量变为原来的 \(d\) 倍,因此放缩倍率为 \(ad\)。当 \(b, c\) 不为 \(0\) 时,可以想象直角坐标系因为 \(b, c\) 的存在而变得不再横平竖直,即矩形被拉成了平行四边形,因此此时变换后的单位面积相较于 \(b, c\)\(0\) 时的变换更小。

    upd:观察一下这个式子,你发现二阶行列式等于列向量的叉积!

  • \(\colorbox{yellow}{\)|A| \neq 0 \iff A \text{满秩} \iff A \text{可逆}\(}\)

    这一点在前文已经详述。你无法从直线反推平面,也无法从平面反推整个三维空间。

  • 单位矩阵行列式为 \(1\)

    相当于没有变换。

  • 上/下三角矩阵的行列式是其主对角线上数值的乘积

    这个可以从 \(O(n!)\) 计算式得出。从最后一行进行倒推,若要对行列式的值产生非 \(0\) 贡献,则必须有 \(p_{n} = n\),然后递归到了一个子问题。

  • \(\colorbox{yellow}{\)|AB| = |A|\times |B|\(}\)

    本质都是对向量空间做两次变换,因此最终放缩的倍数可以看作第一次放缩的倍数乘上第二次放缩的倍数。

    特别地,如果矩阵 \(A\) 可逆,则有 \(\colorbox{yellow}{\)\det(A^{-1})\det(A) = 1\(}\)

  • 行列式的行(列)所有元素等比例变化,则行列式也等比例变化:

    \[\begin{aligned} \left| \begin{matrix} a_{1, 1} & a_{1, 2} & \cdots & k \times a_{1, i} & \cdots & a_{1, n} \\ a_{2, 1} & a_{2, 2} & \cdots & k \times a_{2, i} & \cdots & a_{2, n} \\ \vdots & \vdots & \ddots & \vdots & \ddots & \vdots \\ a_{n, 1} & a_{n, 2} & \cdots & k \times a_{n, i} & \cdots & a_{n, n} \\ \end{matrix} \right| = k \times \left| \begin{matrix} a_{1, 1} & a_{1, 2} & \cdots & a_{1, i} & \cdots & a_{1, n} \\ a_{2, 1} & a_{2, 2} & \cdots & a_{2, i} & \cdots & a_{2, n} \\ \vdots & \vdots & \ddots & \vdots & \ddots & \vdots \\ a_{n, 1} & a_{n, 2} & \cdots & a_{n, i} & \cdots & a_{n, n} \\ \end{matrix} \right| \end{aligned} \]

    这相当于将一个列向量放缩 \(k\) 倍。

    det_mul

  • 见下。

    \[\begin{aligned} \left| \begin{matrix} a_{1, 1} & a_{1, 2} & \cdots & b_{1, i} + c_{1, i} & \cdots & a_{1, n} \\ a_{2, 1} & a_{2, 2} & \cdots & b_{2, i} + c_{2, i} & \cdots & a_{2, n} \\ \vdots & \vdots & \ddots & \vdots & \ddots & \vdots \\ a_{n, 1} & a_{n, 2} & \cdots & b_{n, i} + c_{n, i} & \cdots & a_{n, n} \\ \end{matrix} \right| = \left| \begin{matrix} a_{1, 1} & a_{1, 2} & \cdots & b_{1, i} & \cdots & a_{1, n} \\ a_{2, 1} & a_{2, 2} & \cdots & b_{2, i} & \cdots & a_{2, n} \\ \vdots & \vdots & \ddots & \vdots & \ddots & \vdots \\ a_{n, 1} & a_{n, 2} & \cdots & b_{n, i} & \cdots & a_{n, n} \\ \end{matrix} \right| + \left| \begin{matrix} a_{1, 1} & a_{1, 2} & \cdots & c_{1, i} & \cdots & a_{1, n} \\ a_{2, 1} & a_{2, 2} & \cdots & c_{2, i} & \cdots & a_{2, n} \\ \vdots & \vdots & \ddots & \vdots & \ddots & \vdots \\ a_{n, 1} & a_{n, 2} & \cdots & c_{n, i} & \cdots & a_{n, n} \\ \end{matrix} \right| \end{aligned} \]

    det_add

    将一个列向量进行分解。

  • 交换两列,行列式变号:可以通过前面两个变换进行证明。

  • 进行倍加变换,行列式不变:可以通过前面两个变换进行证明。

  • \(\colorbox{yellow}{\)\det(A) = \det(A^{T})\(}\)

    这意味着对行进行初等变换和对列进行初等变换具有相同的效应,也是 行列式 一词的美妙体现。

    下面是采用将 \(A^{T}\) 分解成初等矩阵的一种证明思路。

    先指出一个易见的事实:初等矩阵的行列式等于其转置矩阵的行列式,即 \(\det(E) = \det(E^{T})\)

    \[\begin{aligned} \det(A^{T}) &= \det(E_{k}^{T} \dots E_{2}^{T}E_{1}^{T}) \\ &= \det(E_{k}^{T}) \dots \det(E_{2}^{T})\det(E_{1}^{T}) \\ &= \det(E_{k}) \dots \det(E_{2})\det(E_{1}) \\ &= \det(E_{1})\det(E_{2}) \dots \det(E_{k}) \\ &= \det(E_{1}E_{2} \dots E_{k}) \\ &= \det(A) \\ \end{aligned} \]


(类)高斯消元求解行列式

\(O(n!)\) 计算式效率太低。在知道行列式的若干性质后,我们考虑用类似高斯消元的方法将矩阵消成三角矩阵(如果不能消成三角矩阵则行列式为 \(0\)),再将对角线上的元素相乘求得行列式,计算量降为 \(O(n^3)\)

具体来讲,我们需要实现的是倍加变换和对换变换,其中前者不改变行列式的值,后者会改变行列式的符号。

tips

  • 当在模意义下运算、且模数不为质数时,可以使用 辗转相除法 进行归零(由势能分析,时间复杂度为 \(O(n^{2}\log{P} + n^{3})\))。

  • 注意对换变换让行列式变号

  • 只用消成三角矩阵即可。

模数为质数的版本:

// Sea, You & Me
struct Plymouth
{
	int mat[N][N];
	int det = 1;
	void Gauss()
	{
		int sgn = 0;
		int r = 1, c = 1;
		for(; c <= n; ++c)
		{
			int p = r;
			for(int i = r; i <= n; ++i)
				if(mat[i][c] != 0)
				{
					p = i;
					break;
				}
			if(mat[p][c] == 0)
				return det = 0, void();
			if(p != r)
			{
				swap(mat[p], mat[r]);
				sgn ^= 1;
			}
			int inv = qwqmi(mat[r][c]);
			for(int i = r + 1; i <= n; ++i)
				if(mat[i][c])
					for(int j = n; j >= c; --j)
						Dec(mat[i][j], mul(mat[i][c], mul(mat[r][j], inv)));
			++r;
		}
		for(int i = 1; i <= n; ++i)
			Mul(det, mat[i][i]);
		if(sgn) det = MOD - det;
	}
};

模数不为质数,使用辗转相除法:

// Sea, You & Me
const int N = 605;
int n, MOD;
struct Plymouth
{
	int mat[N][N], det;
	void Read()
	{
		for(int i = 1; i <= n; ++i)
			for(int j = 1; j <= n; ++j)
				read(mat[i][j]), mat[i][j] %= MOD;
	}	
	void Gauss()
	{
		int sgn = 0;
		int r = 1, c = 1;
		for(; c <= n; ++c)
		{
			int p = r;
			for(int i = r; i <= n; ++i)
				if(mat[i][c] > 0)
				{
					p = i;
					break;
				}
			if(mat[p][c] == 0)
			{
				det = 0;
				return;
			}
			if(p != r)
			{
				sgn ^= 1;
				swap(mat[p], mat[r]);
			}
			for(int i = r + 1; i <= n; ++i)
			{
				while(mat[i][c])
				{
					if(mat[i][c] < mat[r][c])
					{
						sgn ^= 1;
						swap(mat[i], mat[r]);
					}
					int d = mat[i][c] / mat[r][c];
					for(int j = n; j >= c; --j)
						Dec(mat[i][j], mul(d, mat[r][j]));
				}
			}
			++r;
		}
		det = 1;
		for(int i = 1; i <= n; ++i)
			Mul(det, mat[i][i]);
		if(sgn && det) det = MOD - det;
	}
}A;
int main()
{
	read(n);
	read(MOD);
	A.Read();
	A.Gauss();
	printf("%d\n", A.det);	
	return 0;
}

余子式与代数余子式

\(n \times n\) 方阵 \(A\) 的第 \(i\) 行和第 \(j\) 列去掉,剩下的 \((n - 1)^{2}\) 个元素拼成了一个新的矩阵,这个矩阵的行列式即为 \((i, j)\) 元素的 余子式,记作 \(M_{i, j}\)

同时定义 \(C_{i, j} = (-1)^{i + j}M_{i, j}\),作为 \((i, j)\) 元素的 代数余子式

拉普拉斯展开

拉普拉斯展开最初是由范德蒙德给出的(现在被称为行列式的拉普拉斯展开式。这里也只考虑这个特殊形式):

\[\begin{aligned} \colorbox{yellow}{$\det(A) = \sum_{j = 1}^{n}a_{i, j}C_{i, j}, \text{ for all } i = 1, \dots, n$} \\ \end{aligned} \]

我们同时写出莱布尼茨展开式:

\[\begin{aligned} \det(A) = \sum\limits_{{\text{permutation } p}}(-1)^{\mathrm{sgn}(p)}\prod\limits_{i = 1}^{n}a_{i, p_{i}} \\ \end{aligned} \]

可以发现,拉普拉斯展开式相当于枚举排列第 \(i\) 项的值 \(p_{i} = j = 1, \dots, n\),并递归到规模为 \(n - 1\) 的子结构。我们需要计算出剩余部分的行列式,这个行列式即为 余子式 \(M(i, j)\)。而 \(p_{i}\) 这个元素与其它元素之间的大小关系也影响着 \(\det(A)\) 最终的值,系数 \((-1)^{i + j}\)

除此之外,还有事实:(这将帮助你理解伴随矩阵求解逆矩阵的正确性)

\[\begin{aligned} \colorbox{yellow}{$0 = \sum\limits_{j = 1}^{n}a_{i, j}C_{k, j}, \text{ for all } i \neq k$} \\ \end{aligned} \]

对于这一点,考虑将矩阵的第 \(k\) 行加上第 \(i\) 行,行列式不变;然后再按第 \(k\) 行进行拉普拉斯展开:

\[\begin{aligned} |A| = \sum\limits_{j = 1}^{n}(a_{k, j} + a_{i, j})C_{k, j} = \sum\limits_{j = 1}^{n}a_{k, j}C_{k, j} + \sum\limits_{j = 1}^{n}a_{i, j}C_{k, j} = |A| + \sum\limits_{j = 1}^{n}a_{i, j}C_{k, j} \\ \end{aligned} \]

显然 \(\sum\limits_{j = 1}^{n}a_{i, j}C_{k, j} = 0\)

拉普拉斯展开是处理行列式的重要工具。

伴随矩阵

对于 \(n \times n\) 的方阵 \(A\),记 \(C\)\(A\) 的代数余子式矩阵,则 \(A\)伴随矩阵 即为 \(C^{T}\),记作 \(A^{*}\)

如果 \(A\) 是可逆矩阵,则 \(A\) 的逆矩阵可以通过其伴随矩阵 \(A^{*}\) 求解:

\[\begin{aligned} \colorbox{yellow}{$A^{-1} = \frac{A^{*}}{|A|}$} \\ \end{aligned} \]

\(A \times A^{*}\) 结合拉普拉斯展开易得结果为对角线上全为 \(|A|\)、其它位置上全为 \(0\) 的矩阵,这是代数余子式定义的必然结果。

特征值与特征向量

特徵多項式蘊藏的訊息

\(n\) 阶方阵 \(A\),若存在数 \(\lambda\)\(n\) 维非零列向量 \(\vec{x}\) 使得 \(A\vec{x} = \lambda \vec{x}\) 成立,则称 \(\lambda\)\(A\)特征值\(\vec{x}\)\(\lambda\) 对应的 特征向量

几何意义:\(\vec{x}\) 在经过矩阵 \(A\) 的变换后,仍然与之前同向或反向(而当 \(\vec{x}\) 为零向量时,这个定义显然就是无意义的)。由定义:

\[\begin{aligned} A\vec{x} = \lambda \vec{x} \iff (A - \lambda I)\vec{x} = 0 \\ \end{aligned} \]

由于特征值与特征向量必须定义在非零向量上,因此 \(A\) 不能为满秩,否则就无解。

\(|A - \lambda I| = 0\),也即:

\[f(\lambda) = \left| \begin{matrix} a_{1, 1} - \lambda & a_{1, 2} & \cdots & a_{1, n} \\ a_{2, 1} & a_{2, 2} - \lambda & \cdots & a_{2, n} \\ \vdots & \vdots & \ddots & \vdots \\ a_{n, 1} & a_{n, 2} & \cdots & a_{n, n} - \lambda \\ \end{matrix} \right| = 0 \]

\(f(\lambda)\) 是一个关于 \(\lambda\) 的一元 \(n\) 次多项式(其最高次项必为 \((-\lambda)^{n}\)),\(f(\lambda) = 0\) 的若干复根即为 \(A\) 的特征值,代入原式即可求出对应的特征向量。而 \(f(\lambda)\) 被称为 \(A\)特征多项式

我们想具体考察 \(f(\lambda)\) 的系数。通过运用行列式的性质分离 \((-\lambda)\) 的方式,我们可以寻得一丝规律。

\(A\) 为一个 \(3 \times 3\) 的矩阵为例:

\[A = \left[ \begin{matrix} a_{1, 1} & a_{1, 2} & a_{1, 3} \\ a_{2, 1} & a_{2, 2} & a_{2, 3} \\ a_{3, 1} & a_{3, 2} & a_{3, 3} \\ \end{matrix} \right] \]

其特征多项式为:

\[f(\lambda) = \left| \begin{matrix} a_{1, 1} - \lambda & a_{1, 2} & a_{1, 3} \\ a_{2, 1} & a_{2, 2} - \lambda & a_{2, 3} \\ a_{3, 1} & a_{3, 2} & a_{3, 3} - \lambda \\ \end{matrix} \right| \]

将第一行进行分离:

\[f(\lambda) = \left| \begin{matrix} -\lambda & 0 & 0 \\ a_{2, 1} & a_{2, 2} - \lambda & a_{2, 3} \\ a_{3, 1} & a_{3, 2} & a_{3, 3} - \lambda \\ \end{matrix} \right| + \left| \begin{matrix} a_{1, 1} & a_{1, 2} & a_{1, 3} \\ a_{2, 1} & a_{2, 2} - \lambda & a_{2, 3} \\ a_{3, 1} & a_{3, 2} & a_{3, 3} - \lambda \\ \end{matrix} \right| \\ \]

将第二行进行分离:

\[f(\lambda) = \left| \begin{matrix} -\lambda & 0 & 0 \\ 0 & -\lambda & 0 \\ a_{3, 1} & a_{3, 2} & a_{3, 3} - \lambda \\ \end{matrix} \right| + \left| \begin{matrix} -\lambda & 0 & 0 \\ a_{2, 1} & a_{2, 2} & a_{2, 3} \\ a_{3, 1} & a_{3, 2} & a_{3, 3} - \lambda \\ \end{matrix} \right| + \left| \begin{matrix} a_{1, 1} & a_{1, 2} & a_{1, 3} \\ 0 & -\lambda & 0 \\ a_{3, 1} & a_{3, 2} & a_{3, 3} - \lambda \\ \end{matrix} \right| + \left| \begin{matrix} a_{1, 1} & a_{1, 2} & a_{1, 3} \\ a_{2, 1} & a_{2, 2} & a_{2, 3} \\ a_{3, 1} & a_{3, 2} & a_{3, 3} - \lambda \\ \end{matrix} \right| \]

将第三行进行分离:

\[f(\lambda) = \left| \begin{matrix} -\lambda & 0 & 0 \\ 0 & -\lambda & 0 \\ 0 & 0 & -\lambda \\ \end{matrix} \right| + \left| \begin{matrix} -\lambda & 0 & 0 \\ 0 & -\lambda & 0 \\ a_{3, 1} & a_{3, 2} & a_{3, 3} \\ \end{matrix} \right| + \left| \begin{matrix} -\lambda & 0 & 0 \\ a_{2, 1} & a_{2, 2} & a_{2, 3} \\ 0 & 0 & -\lambda \\ \end{matrix} \right| + \left| \begin{matrix} -\lambda & 0 & 0 \\ a_{2, 1} & a_{2, 2} & a_{2, 3} \\ a_{3, 1} & a_{3, 2} & a_{3, 3} \\ \end{matrix} \right| \\ + \left| \begin{matrix} a_{1, 1} & a_{1, 2} & a_{1, 3} \\ 0 & -\lambda & 0 \\ 0 & 0 & -\lambda \\ \end{matrix} \right| + \left| \begin{matrix} a_{1, 1} & a_{1, 2} & a_{1, 3} \\ 0 & -\lambda & 0 \\ a_{3, 1} & a_{3, 2} & a_{3, 3} \\ \end{matrix} \right| + \left| \begin{matrix} a_{1, 1} & a_{1, 2} & a_{1, 3} \\ a_{2, 1} & a_{2, 2} & a_{2, 3} \\ 0 & 0 & -\lambda \\ \end{matrix} \right| + \left| \begin{matrix} a_{1, 1} & a_{1, 2} & a_{1, 3} \\ a_{2, 1} & a_{2, 2} & a_{2, 3} \\ a_{3, 1} & a_{3, 2} & a_{3, 3} \\ \end{matrix} \right| \]

对于只含一个 \(\lambda\)、其余元素均为 \(0\) 的行使用拉普拉斯展开,最终可以得到:

\[\begin{aligned} f(\lambda) = (-\lambda)^{3} + (a_{1, 1} + a_{2, 2} + a_{3, 3})(-\lambda)^{2} + ( \left| \begin{matrix} a_{1, 1} & a_{1, 2} \\ a_{2, 1} & a_{2, 2} \\ \end{matrix} \right| + \left| \begin{matrix} a_{1, 1} & a_{1, 3} \\ a_{3, 1} & a_{3, 3} \\ \end{matrix} \right| + \left| \begin{matrix} a_{2, 2} & a_{2, 3} \\ a_{3, 2} & a_{3, 3} \\ \end{matrix} \right| )(-\lambda) + \left| \begin{matrix} a_{1, 1} & a_{1, 2} & a_{1, 3} \\ a_{2, 1} & a_{2, 2} & a_{2, 3} \\ a_{3, 1} & a_{3, 2} & a_{3, 3} \\ \end{matrix} \right| \end{aligned} \]

接下来引入更加简洁的表示方法。

保留 \(A\)\(k\) 个相同的列指标与行指标可得到一个 \(k \times k\) 的子矩阵,这样的子矩阵被称为 \(A\)\(k\)主子矩阵,其行列式被称为 \(A\)\(k\)主子式

对于 \(n \times n\) 的方阵 \(A\),显然有 \(\binom{n}{k}\)\(k\) 阶主子矩阵,对应了 \(\binom{n}{k}\) 个主子式,我们把这些主子式之和记作 \(E_{k}(A)\)。其中 \(E_{1}(A)\) 被称为矩阵 \(A\),我们记:

\[\begin{aligned} E_{1}(A) = a_{1, 1} + a_{2, 2} + \dots + a_{n, n} = \mathrm{trace}(A) \\ \end{aligned} \]

还有一个较为特殊的是 \(E_{n} = \det(A)\)

我们可以通过 \(E\) 来表示 \(f(\lambda)\)

\[\begin{aligned} f(\lambda) = (-\lambda)^{n} + E_{1}(A)(-\lambda)^{n - 1} + E_{2}(A)(-\lambda)^{n - 2} + \dots + E_{n - 1}(-t) + E_{n}(A) \\ \end{aligned} \]

\(A\) 满秩时,\(E_{n}(A) = \det(A) \neq 0\),故 \(f(\lambda) = 0\) 无解,由此也可见特征家族对可逆矩阵是没有意义的。

总结一下,通过对 \((a_{i, i} - \lambda)\) 的拆解进行组合,取出 \(n - k\)\(-\lambda\) 决定次数(和系数),同时以所有 \(k\) 阶主子式之和决定系数,得到 \(f(\lambda)\) 的简洁表达。这简直可以称为 “矩阵中的二项式定理” 了。


接下来我们要讨论的是 \(f(\lambda) = 0\) 的解,即特征值。

\(\lambda_{1}, \lambda_{2}, \dots, \lambda_{n}\)\(f(\lambda) = 0\)\(n\) 个复根(可能存在重根),并将 \(f(\lambda)\) 分解:

\[\begin{aligned} f(\lambda) = (\lambda_{1} - \lambda)(\lambda_{2} - \lambda) \dots (\lambda_{n} - \lambda) \\ \end{aligned} \]

\(L = \{\lambda_{1}, \lambda_{2}, \dots, \lambda_{n} \}\),并记 基本对称函数

\[\begin{aligned} S_{k}(L) = \sum\limits_{1 \le p_{1} < p_{2} < \dots < p_{k} \le n}\prod\limits_{i = 1}^{k}\lambda_{p_{i}} \\ \end{aligned} \]

则:

\[\begin{aligned} f(\lambda) = (-\lambda)^{n} + S_{1}(L)(-\lambda)^{n - 1} + S_{2}(L)(-\lambda)^{n - 2} + \dots + S_{n - 1}(L)(-\lambda) + S_{n}(L) \\ \end{aligned} \]

发现 \(E\)\(S\) 可以建立联系!

\[\begin{aligned} E_{k}(A) = S_{k}(L), k = 1, 2, \dots, n \\ \end{aligned} \]

\(k = 1\) 时,有:

\[\begin{aligned} \colorbox{yellow}{$\mathrm{trace}(A) = \sum\limits_{i = 1}^{n}\lambda_{i}$} \\ \end{aligned} \]

\(k = n\) 时,有:

\[\begin{aligned} \colorbox{yellow}{$\det(A) = \prod\limits_{i = 1}^{n}\lambda_{i}$} \\ \end{aligned} \]

范德蒙德矩阵

\[V_{n} = \left[ \begin{matrix} 1 & x_{1} & \cdots & x_{1}^{n - 1} \\ 1 & x_{2} & \cdots & x_{2}^{n - 1} \\ \vdots & \vdots & \ddots & \vdots \\ 1 & x_{n} & \cdots & x_{n}^{n - 1} \\ \end{matrix} \right] \]

\(V_{n}^{T}\) 也称为范德蒙德矩阵。

范德蒙德矩阵的行列式

\[\begin{aligned} \colorbox{yellow}{$\det(V_{n}) = \prod\limits_{1 \le j < i \le n}(x_{i} - x_{j})$} \\ \end{aligned} \]

采用归纳法证明。

首先,如果存在 \(x_{i} = x_{j}, i \neq j\),则行列式显然为 \(0\)

否则假设 \(k\) 阶范德蒙德矩阵的行列式满足上述关系,并考察 \(k + 1\) 阶范德蒙德矩阵的行列式:

\[\begin{aligned} \det(V_{k + 1}) = \left| \begin{matrix} 1 & x_{1} & \cdots & x_{1}^{k} \\ 1 & x_{2} & \cdots & x_{2}^{k} \\ \vdots & \vdots & \ddots & \vdots \\ 1 & x_{k + 1} & \cdots & x_{k + 1}^{k} \\ \end{matrix} \right| \end{aligned} \]

对最后一行使用拉普拉斯展开,可以得到 \(\det(V_{k + 1})\) 是一个关于 \(x_{k + 1}\)\(k\) 次多项式 \(f(x)\),并且 \(x_{1}, x_{2}, \dots, x_{k}\) 必然为 \(f(x) = 0\) 的根。

于是我们可以将行列式写成关于 \(x_{k + 1}\) 的多项式 \(f(x)\)

\[\begin{aligned} \det(V_{k + 1}) = f(x) = \alpha(x - x_{1})(x - x_{2})\dots(x - x_{k}) \\ \end{aligned} \]

注意到 \(\alpha\) 即为 \(M_{k + 1, k + 1} = \det(V_{k}) = \prod\limits_{1 \le j < i \le k}(x_{i} - x_{j})\)。回代:

\[\begin{aligned} \det(V_{k + 1}) = f(x_{k + 1}) &= \left(\prod\limits_{1 \le j < i \le k}(x_{i} - x_{j}) \right)(x_{k + 1} - x_{1})(x_{k + 1} - x_{2})\dots(x_{k + 1} - x_{k}) \\ &= \prod\limits_{1 \le j < i \le k + 1}(x_{i} - x_{j}) \\ \end{aligned} \]

即所证。

范德蒙德矩阵的逆矩阵

考虑求解 \(n \times n\) 的范德蒙德矩阵 \(V\) 的逆矩阵。

前提是保证 \(V\) 是可逆矩阵,即 \(|V| \neq 0\)

Solution 1

采用 \(V^{-1} = \frac{V^{*}}{|V|}\) 进行求解。由于上文已经给出了 \(|V|\) 的计算式,因此只需算出 \(V^{*}\)​。

为了方便表示,将 \((x_{1}, \dots, x_{i - 1}, x_{i + 1}, \dots, x_{n})\) 记作 \((z_{1}, \dots, z_{n - 1})\)

\(W_{n - 1}^{(j - 1)}(z_{1}, \dots, z_{n - 1})\) 表示去掉第 \(i\) 行和第 \(j\) 列后的矩阵。

由伴随矩阵的定义,有 \((V^{*})_{i, j} = (-1)^{i + j}\det(W_{n - 1}^{(j - 1)}(z_{1}, \dots, z_{n - 1}))\)

引入形式变量 \(t\)\(t \neq z_{i}\)),构成 \(n \times n\) 范德蒙德矩阵 \(V_{n}(z_{1}, \dots, z_{n - 1}, t)\)

\[\det(V_{n}(z_{1}, \dots, z_{n - 1}, t)) = \left| \begin{matrix} 1 & z_{1} & \cdots & z_{1}^{n - 1} \\ 1 & z_{2} & \cdots & z_{2}^{n - 1} \\ \vdots & \vdots & \ddots & \vdots \\ 1 & z_{n - 1} & \cdots & z_{n - 1}^{n - 1} \\ 1 & t & \cdots & t^{n - 1} \end{matrix} \right| \]

接下来 利用范德蒙德行列式的直接计算式与对形式变量 \(t\) 的拉普拉斯展开建立相同结构并进行联系

第一式:利用公式 \(\det(V_{n}) = \prod\limits_{1 \le j < i \le n}(x_{i} - x_{j})\)

\[\begin{aligned} \det(V_{n}(z_{1}, \dots, z_{n - 1}, t)) &= \left( \prod\limits_{1 \le j < i \le n - 1}(z_{j} - z_{i}) \right) \left( \prod\limits_{1 \le i \le n - 1}(t - z_{i}) \right) \\ &= \det(V_{n - 1}(z_{1}, \dots, z_{n - 1}))\prod\limits_{1 \le i \le n - 1}(t - z_{i}) \\ \end{aligned} \]

引入求解特征值时提到的基本对称函数 \(S_{k}(z_{1}, \dots, z_{n - 1}) = \sum\limits_{1 \le p_{1} < p_{2} < \dots < p_{k} \le n - 1}\prod\limits_{i = 1}^{k}z_{p_{i}}\)

\[\begin{aligned} \det(V_{n}(z_{1}, \dots, z_{n - 1}, t)) &= \det(V_{n - 1}(z_{1}, \dots, z_{n - 1}))\left( \sum\limits_{i = 0}^{n - 1}(-1)^{i}S_{i}(z_{1}, \dots, z_{n - 1})t^{n - i - 1} \right) \\ \end{aligned} \]

第二式:对最后一行使用拉普拉斯展开:

\[\begin{aligned} \det(V_{n}(z_{1}, \dots, z_{n - 1}, t)) = \sum\limits_{i = 0}^{n - 1}(-1)^{n - 1 - i}\det(W_{n - 1}^{(i)}(z_{1}, \dots, z_{n - 1}))t^{i} \\ \end{aligned} \]

发现多项式系数可以一一对应,联立得:

\[\begin{aligned} \det(W_{n - 1}^{i}(z_{1}, \dots, z_{n - 1})) = \det(V_{n - 1}(z_{1}, \dots, z_{n - 1}))S_{n - 1 - i}(z_{1}, \dots, z_{n - 1}), i = 0, \dots, n - 1 \\ \end{aligned} \]

代回逆矩阵:

\[\begin{aligned} (V^{-1})_{i, j} &= \frac{(V^{*})_{i, j}}{|V|} \\ &= \frac{(-1)^{i + j}\det(V_{n - 1}(z_{1}, \dots, z_{n - 1}))S_{n - j}(z_{1}, \dots, z_{n - 1})}{|V|} \\ &= (-1)^{i + j}\left( \sum\limits_{1 \le p_{1} < p_{2} < \dots < p_{n - j} \le n - 1}\prod\limits_{k = 1}^{n - j}z_{p_{k}} \right)\left( \prod\limits_{1 \le k < i}(x_{i} - x_{k}) \right)\left( \prod\limits_{i < k \le n}(x_{k} - x_{i}) \right) \\ &= \colorbox{yellow}{$(-1)^{j + 1}\left( \sum\limits_{1 \le p_{1} < p_{2} < \dots < p_{n - j} \le n - 1}\prod\limits_{k = 1}^{n - j}z_{p_{k}} \right)\left( \prod\limits_{1 \le k \le n, k \neq i}(x_{k} - x_{i}) \right)$} \\ \end{aligned} \]

Solution 2

高斯消元入门

高斯消元、矩阵求逆和行列式求值的模板放在了前文的基础知识中,这里不再赘述。

高斯消元求解异或方程组及其 bitset 优化

P3164 [CQOI2014] 和谐矩阵

Problem

我们称一个由 \(0\)\(1\) 组成的矩阵是和谐的,当且仅当每个元素都有偶数个相邻的 \(1\)。一个元素相邻的元素包括它本身,及他上下左右的 \(4\) 个元素(如果存在)。给定矩阵的行数和列数,请计算并输出一个和谐的矩阵。注意:所有元素为 \(0\) 的矩阵是不允许的。

\(1 \le n, m \le 40\)

Solution

对于一个合法的矩阵 \(A\),题目条件可以直接转化为(越界的项直接忽视):

\[\begin{aligned} a_{x, y} \text{ xor } a_{x + 1, y} \text{ xor } a_{x - 1, y} \text{ xor } a_{x, y + 1} \text{ xor } a_{x, y - 1} = 0 \\ \end{aligned} \]

状态数是 \(O(nm)\) 的,那么直接高斯消元的时间复杂度是 \(O((nm)^{3})\) 的。

此时可以使用 bitset 优化,实现如下:

bitset<M> mat[M];
int row[M];
void Print()
{
    for(int i = 1; i <= n * m; ++i)
    {
        for(int j = 1; j <= n * m; ++j)
            cerr << mat[i][j] << ' ';
        cerr << '\n';
    }
}
void doit()
{
    int r = 1, c = 1;
    for(; c <= n * m; ++c)
    {
        int p = r;
        for(int i = r; i <= n * m; ++i)
            if(mat[i][c])
            {
                p = i;
                break;
            }
        if(!mat[p][c])
        {
            ans[c] = 1;
            continue;
        }
        swap(mat[p], mat[r]);
//			cerr << r << ' ' << c << '\n';
        row[c] = r;
        for(int i = 1; i <= n * m; ++i)
        {
            if(i == r) continue;
            if(mat[i][c]) mat[i] ^= mat[r];
        }
        ++r;
//			Print();
    }
    for(int i = 1; i <= n * m; ++i)
    {
        if(ans[i]) continue;
        int r = row[i];
        for(int j = 1; j <= n * m; ++j)
        {
            if(!mat[r][j]) continue;
            ans[i] ^= ans[j];
        }
    }
}

就是这个题让我对高斯消元对多解方程组求解的情况进行了一系列思考。

P2447 [SDOI2010] 外星千足虫

Problem

给定 \(n\) 个变量和 \(m\) 个异或方程,求最少需要多少个才能确定每个变量的解,或报告多解。

Solution

选取编号最小的一行进行交换并更新答案即可。贪心正确性有时间再来说明。

高斯消元求解带环 DP

一种常见的类型是图上的随机游走,这类问题通常与概率论密不可分,期望的线性性 是一个必须掌握的性质。

稍微困难一点的题目会涉及到针对特定 DP 转移式的对高斯消元的优化,比如减少状态、带状矩阵的优化等。其中带状矩阵的优化将在下一节中单独提及。

CF113D Museum

洛谷:CF113D Museum

Codeforces:CF113D Museum

Problem

有一个 \(n\) 个点 \(m\) 条边的无向连通图,两人分别从 \(a, b\) 出发,每个人每分钟有 \(p_{i}\) 的概率不动,有 \(1 - p_{i}\) 的概率走到随机一个相邻的点。

当他们在同一时刻到达同一个房间,他们就会在那个房间相遇并停止。求在每个点相遇的概率。

\(1 \le n \le 22\)\(n - 1 \le m \le \frac{n(n - 1)}{2}\)\(1 \le a, b \le n\)\(0.01 \le p_{i} \le 0.99\)

Solution

一个关键的思想是:概率可以转为期望

具体地,在本题中,当两个人同时走到某个点时,他们必然停止移动,因此两人同时在某个点停止的概率等于两人同时走到那个点的期望次数。

我们可以设 \(f_{x, y}\) 表示一个人位于 \(x\),另一个人位于 \(y\) 的期望出现次数。

\(N(x)\) 表示与 \(x\) 相邻的点的集合,\(d_{x}\) 表示 \(x\) 的度数,则有转移:

\[\begin{aligned} f_{x, y} - [x = a \land y = b] = \sum\limits_{u \in N(x), v \in N(y), u \neq v}\frac{(1 - p_{u})(1 - p_{v})}{d_{u} \times d_{v}}f_{u, v} + \sum\limits_{u \in N(x), u \neq y}\frac{(1 - p_{u})p_{y}}{d_{u}}f_{u, y} + \sum\limits_{v \in N(y), x \neq v}\frac{p_{x}(1 - p_{v})}{d_{v}}f_{x, v} + [x \neq y]p_{x}p_{y}f_{x, y} \\ \end{aligned} \]

注意状态 \((i, i)\) 不能拿来转移。

然后做高斯消元即可,总状态数为 \(O(n^{2})\),时间复杂度 \(O(n^{6})\)

code CF113D

P2973 [USACO10HOL] Driving Out the Piggies G

Problem

给定一个点数为 \(n\) 的无向图,节点 \(1\) 有一个炸弹,在每个单位时间内,有 \(\frac{p}{q}\) 的概率在这个节点炸掉,有 \(1 - \frac{p}{q}\) 的概率随机选择一条出去的路到其他的节点上。问最终炸弹在每个节点上爆炸的概率。\(2 \le n \le 300\)

Solution

\(f_{x}\) 表示到达 \(x\) 的期望次数,则在节点 \(x\) 上爆炸的概率为 \(f_{x} \times \frac{p}{q}\)(正确性:如果随机变量 \(X, Y\) 独立,则 \(E[XY] = E[X]E[Y]\))。

\(N(x)\) 表示与 \(x\) 相邻的点的集合,\(d_{x}\) 表示 \(x\) 的度数,则有转移:

\[\begin{aligned} f_{x} - [x = 1] = \sum\limits_{u \in N(x)}(1 - \frac{p}{q})\frac{1}{d_{u}}f_{u} \\ \end{aligned} \]

状态数 \(O(n)\),时间复杂度 \(O(n^3)\)

P3232 [HNOI2013] 游走

Problem

给定一个 \(n\) 个点 \(m\) 条边的无重边、无自环的无向连通图,顶点从 \(1\) 编号到 \(n\),边从 \(1\) 编号到 \(m\)

小 Z 在该图上进行随机游走,初始时小 Z 在 \(1\) 号顶点,每一步小 Z 以相等的概率随机选择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小 Z 到达 \(n\) 号顶点时游走结束,总分为所有获得的分数之和。 现在,请你对这 \(m\) 条边进行编号,使得小 Z 获得的总分的期望值最小。求这个最小期望值。

\(2 \le n \le 500\)\(1 \le m \le 125000\)

Solution

由于 \(n \le 500\),因此还是考虑对点的信息建立方程进行高斯消元。

套路地记 \(f_{x}\) 表示到达 \(x\) 的期望次数,记 \(N(x)\) 表示与 \(x\) 相邻的点的集合,\(d_{x}\) 表示 \(x\) 的度数,则有转移:

\[\begin{aligned} f_{x} - [x = 1] = \sum\limits_{u \in N(x), u \neq n}\frac{1}{d_{u}}f_{u} \\ \end{aligned} \]

发现边的期望经过次数可以由 \(f\) 表示出来。具体地,边 \((u, v)\) 的期望经过次数为:

\[\begin{aligned} \frac{[u \neq n]f_{u}}{d_{u}} + \frac{[v \neq n]f_{v}}{d_{v}} \\ \end{aligned} \]

贪心地将期望出现次数大的与小的边权配对即可。

时间复杂度为 \(O(n^{3} + m\log{m})\)

P3211 [HNOI2011] XOR和路径

Problem

给定一个无向连通图,其节点编号为 \(1\)\(n\),其边的权值为非负整数,试求 \(1 \to n\) 路径的异或和期望值。该路径可以重复经过某些节点或边,当一条边在路径中出现多次时,其权值在计算异或和时也要被重复计算相应多的次数。

\(2 \le n \le 100\)

Solution

由于二进制下各位独立,因此考虑拆位处理,这样就把异或运算转成了加减运算。

\(f_{p}(x)\) 表示从 \(x\) 出发走到 \(n\)\(p\) 位的期望值(不考虑乘 \(2^{p}\)),其实也等价于第 \(p\) 位为 \(1\) 的概率。

\(d_{x}\) 表示 \(x\) 的度数,三元组 \((u, v, w)\) 表示 \(u\)\(v\) 之间一条权值第 \(p\) 位为 \(w = 0 / 1\) 的无向边(\(u, v\) 无序),则有转移:

\[\begin{aligned} f_{p}(x) = \sum\limits_{(x, u, w), w = 0}\frac{f_{p}(u)}{d_{x}} + \sum\limits_{(x, u, w), w = 1}\frac{1 - f_{p}(u)}{d_{x}} \text{ for all } x, 1\le x < n \\ \end{aligned} \]

注意 \(f_{p}(n)\) 恒为 \(0\),以及一个自环只能算一遍。最终答案为:

\[\begin{aligned} \sum\limits_{p = 0}2^{p}f_{p}(1) \\ \end{aligned} \]

时间复杂度为 \(O(n^{3}\log{V})\)

thinking:为什么这道题与前面例题的递推方向是相反的?因为正推不能保证起点为 \(1\),但之前的题正推已经把起点的限制容纳在了转移式中。

【Trick】带状矩阵的高斯消元

也不乏有题目会在高斯消元本身下功夫。

存在这样的情况:高斯消元的系数矩阵长宽很大,但非 \(0\) 的值只分布在对角线两侧的常数个

矩阵树定理

说明:只给结论,不予证明。

基础知识

基本上就是把 OI-wiki 上的内容抄了一遍。

矩阵树定理用于解决一张图的生成树计数的问题

该笔记中的图,无论无向还是有向,都允许重边,但是 不允许自环(其实有自环可以直接忽视掉)。

无向图上的定义

\(G\) 是一个有 \(n\) 个顶点的无向图。

定义 \(t(G)\)\(G\) 的生成树个数。

Degree Matrix

定义度数矩阵 \(D(G)\) 为:

\[D_{i,i}(G)=\operatorname{deg}(i),D_{i,j}(G)=0,i\neq j \]

即:矩阵的 \((i,i)\) 位置上的值是点 \(i\) 的度数,其他位置上值为 \(0\)

Adjacency Matrix

定义 \(\#e(i,j)\) 为点 \(i\) 与点 \(j\) 相连的边数,并定义邻接矩阵 \(A(G)\) 为:

\[A_{i,j}(G)=A_{j,i}(G)=\#e(i,j) \]

Laplacian Matrix / Kirchoff Matrix

定义基尔霍夫矩阵 \(L(G)\) 为:

\[L(G)=D(G)-A(G) \]

即度数矩阵和邻接矩阵的对应位置相减。

有向图上的定义

\(G\) 是一个有 \(n\) 个顶点的有向图。

\(t^{root}(G,r)\) 为图 \(G\) 的以 \(r\) 为根的 根向树形图(内向树) 的个数。

\(t^{leaf}(G,r)\) 为图 \(G\) 的以 \(r\) 为根的 叶向树形图(外向树) 的个数。

Out-degree Matrix

定义出度矩阵 \(D^{out}(G)\) 为:

\[D^{out}_{i,i}(G)=\operatorname{deg}^{out}(i),D^{out}_{i,j}(G)=0,i\ne j \]

In-degree Matrix

定义入度矩阵 \(D^{in}(G)\) 为:

\[D^{in}_{i,i}(G)=\operatorname{deg}^{in}(i),D^{in}_{i,j}(G)=0,i\ne j \]

Adjacency Matrix

\(\#e(i,j)\) 为点 \(i\) 指向点 \(j\)有向 边数,并定义邻接矩阵 \(A(G)\) 为:

\[A_{i,j}=\#e(i,j) \]

Out-degree Laplacian Matrix

定义出度 Laplace 矩阵 \(L^{out}(G)\) 为:

\[L^{out}(G)=D^{out}(G)-A(G) \]

In-degree Laplacian Matrix

定义入度 Laplace 矩阵 \(L^{in}(G)\) 为:

\[L^{in}(G)=D^{in}(G)-A(G) \]

矩阵树定理

定理1(矩阵树定理,无向图行列式形式)

对任意 \(1 \le i \le n\) ,都有:

\[\begin{aligned} t(G)= M(L(G))_{i, i} \\ \end{aligned} \]

即拉普拉斯矩阵对角线上的余子式。

定理2(矩阵树定理,无向图特征值形式)

定理3(矩阵树定理,有向图根向形式)& 定理4(矩阵树定理,有向图叶向形式)

对任意 \(1 \le i \le n\)

根向树形图计数:

\[\begin{aligned} t^{root}(G,i) = M(L^{out}(G))_{i, i} \\ \end{aligned} \]

叶向树形图计数:

\[\begin{aligned} t^{leaf}(G,i) = M(L^{in}(G))_{i, i} \\ \end{aligned} \]

若要计算一张图所有的根/叶向树形图的数量,枚举所有的点作为根计数即可。

注意:内向树(即根向树形图)的计数是用出度拉普拉斯矩阵计算的,外向树(即叶向树形图)的计数是用入度拉普拉斯矩阵计算的,刚好相反

矩阵树定理求解的另一个意义是:对于一个边带边权的图,求 所有生成树边权乘积之和。即:

\[\begin{aligned} \colorbox{yellow}{$\sum\limits_{T}\sum\limits_{e \in T}w_{e}$} \\ \end{aligned} \]

P3317 [SDOI2014] 重建

Problem

\(n\) 个点,每两个点之间都有一定概率 \(p \in [0, 1]\) 出现在图中,求生成的图恰为一棵树的概率。

Solution

直接写出答案式,并进行推导使得式子化成矩阵树定理所求的形式:

\[\begin{aligned} ans &= \sum\limits_{T}\left( \prod\limits_{e \in T}p_{e}\prod\limits_{e \not\in T}(1 - p_{e}) \right) \\ &= \sum\limits_{T}\left( \prod\limits_{e \in T}p_{e}\frac{\prod\limits_{e}(1 - p_{e})}{\prod\limits_{e \in T}(1 - p_{e})} \right) \\ &= \prod\limits_{e}(1 - p_{e})\sum\limits_{T}\prod\limits_{e \in T}\frac{p_{e}}{1 - p_{e}} \\ \end{aligned} \]

\(\frac{p_{e}}{1 - p_{e}}\) 视作边权即可。

P4336 [SHOI2016] 黑暗前的幻想乡

Problem

\(n\) 个点 \(m\) 条边,边有颜色,共有 \(n - 1\) 种颜色。求异色生成树个数。\(2 \le n \le 17\)

Solution

直接容斥计算即可。具体地,枚举颜色集合 \(S\),保留颜色在 \(S\) 内的边,计算出生成树数量为 \(w\),则贡献为 \((-1)^{n - 1 - |S|}w\)

时间复杂度 \(O(2^{n}n^{3})\)

P4455 [CQOI2018] 社交网络

Problem

给定 \(n\) 个点 \(m\) 条有向边,求以 \(1\) 号点为根的外向树数量。\(1 \le n \le 250\)

Solution

板子。

P6624 [省选联考 2020 A 卷] 作业题

Problem

给定一个 \(n\) 个顶点 \(m\) 条边(点和边都从 \(1\) 开始编号)的无向图 \(G\),保证图中无重边和无自环。每一条边有一个正整数边权 \(w_i\),对于一棵 \(G\) 的生成树 \(T\),定义 \(T\) 的价值为:\(T\) 所包含的边的边权的最大公约数乘以边权之和,即:

\[\begin{aligned} val(T)=\left(\sum\limits_{i=1}^{n-1} w_{e_i}\right) \times \gcd(w_{e_1},w_{e_2},\dots,w_{e_{n-1}}) \\ \end{aligned} \]

其中 \(e_1,e_2,\dots,e_{n-1}\)\(T\) 包含的边的编号。

求出 \(G\) 的所有生成树 \(T\) 的价值之和。由于答案可能很大,你只需要给出答案对 \(998244353\) 取模后的结果。

\(1 \le n \le 30\)

Solution

用数论变换把 \(\gcd\) 搞掉(这里考虑使用欧拉反演),试图化出矩阵树定理所求的形式:

\[\begin{aligned} ans &= \sum\limits_{T}\left( \sum\limits_{i = 1}^{n - 1}w_{e_{i}} \times \sum\limits_{d, d | w_{e_{1}}, d | w_{e_{2}}, \dots, d | w_{e_{n - 1}}}\varphi(d) \right) \\ &= \sum\limits_{d = 1}\varphi(d)\left( \sum\limits_{T, d | w_{e_{1}}, d | w_{e_{2}}, \dots, d | w_{e_{n - 1}}}\sum\limits_{i = 1}^{n - 1}w_{e_{i}} \right) \\ \end{aligned} \]

发现我们现在需要求的是一个(子)图中 所有生成树边权和之和,而不是所有生成树边权乘积之和。

这里就要用到 生成函数 的观点,把边权 \(w\) 变作多项式 \(wx + 1\),则对新的边权使用矩阵树定理求出来的结果(是一个多项式)的一次项系数即为所有生成树边权和之和。从组合意义上讲,这相当于枚举产生贡献的边,并统计这条边在多少个生成树中出现。

此时直接枚举 \(d\)、然后保留所有权值是 \(d\) 的倍数的边可以得到 \(O(n^{3}w_{\max})\) 的做法。

注意到有时图是不连通的,因此可以直接跳过。加上这个优化后就跑得很稳了。时间复杂度有空再分析。

P2144 [FJOI2007] 轮状病毒

Problem

一个 \(n\) 轮状基由圆环上 \(n\) 个不同的基原子和圆心的一个核原子构成。\(2\) 个原子之间的边表示这 \(2\) 个原子之间的信息通道。

\(n\) 轮状病毒的产生规律是在 \(n\) 轮状基中删除若干边,使各原子之间有唯一一条信息通道。

给定 \(n\ (n\le100)\),编程计算有多少个不同的 \(n\) 轮状病毒。

Solution

一眼矩阵树定理,但是没有取模!这说明要写高精度。

注意到这个图非常有规律,因此不妨手玩一下行列式。

将核原子编号为 \(1\),其余原子编号 \(2 \sim n + 1\),可以直接写出 \(n \times n\) 拉普拉斯矩阵:

\[\left[ \begin{matrix} n & -1 & -1 & -1 & -1 & -1 & \cdots & -1 & -1 & -1 \\ -1 & 3 & -1 & 0 & 0 & 0 & \cdots & 0 & 0 & -1 \\ -1 & -1 & 3 & -1 & 0 & 0 & \cdots & 0 & 0 & 0 \\ -1 & 0 & -1 & 3 & -1 & 0 & \cdots & 0 & 0 & 0 \\ -1 & 0 & 0 & -1 & 3 & -1 & \cdots & 0 & 0 & 0 \\ \vdots & \vdots & \vdots & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ -1 & 0 & 0 & 0 & 0 & 0 & \cdots & -1 & 3 & -1 \\ -1 & -1 & 0 & 0 & 0 & 0 & \cdots & 0 & -1 & 3 \\ \end{matrix} \right]_{(n + 1) \times (n + 1)} \]

核原子过于独特,因此求余子式肯定选择 \(M(L(G))_{1, 1}\)

\[ans_{n} = \left| \begin{matrix} 3 & -1 & 0 & 0 & 0 & \cdots & 0 & 0 & -1 \\ -1 & 3 & -1 & 0 & 0 & \cdots & 0 & 0 & 0 \\ 0 & -1 & 3 & -1 & 0 & \cdots & 0 & 0 & 0 \\ 0 & 0 & -1 & 3 & -1 & \cdots & 0 & 0 & 0 \\ \vdots & \vdots & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ 0 & 0 & 0 & 0 & 0 & \cdots & -1 & 3 & -1 \\ -1 & 0 & 0 & 0 & 0 & \cdots & 0 & -1 & 3 \\ \end{matrix} \right|_{n \times n} \]

想把两个角上的 \(-1\) 消掉,这里选择对第一行进行拉普拉斯展开,得到:

\[ans_{n} = 3\times \left| \begin{matrix} 3 & -1 & 0 & 0 & \cdots & 0 & 0 & 0 \\ -1 & 3 & -1 & 0 & \cdots & 0 & 0 & 0 \\ 0 & -1 & 3 & -1 & \cdots & 0 & 0 & 0 \\ \vdots & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ 0 & 0 & 0 & 0 & \cdots & -1 & 3 & -1 \\ 0 & 0 & 0 & 0 & \cdots & 0 & -1 & 3 \\ \end{matrix} \right|_{(n - 1)\times (n - 1)} + \\ \left| \begin{matrix} -1 & -1 & 0 & 0 & \cdots & 0 & 0 & 0 \\ 0 & 3 & -1 & 0 & \cdots & 0 & 0 & 0 \\ 0 & -1 & 3 & -1 & \cdots & 0 & 0 & 0 \\ \vdots & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ 0 & 0 & 0 & 0 & \cdots & -1 & 3 & -1 \\ -1 & 0 & 0 & 0 & \cdots & 0 & -1 & 3 \\ \end{matrix} \right|_{(n - 1)\times (n - 1)} + (-1)^{n}\left| \begin{matrix} -1 & 3 & -1 & 0 & 0 & \cdots & 0 & 0 \\ 0 & -1 & 3 & -1 & 0 & \cdots & 0 & 0 \\ 0 & 0 & -1 & 3 & -1 & \cdots & 0 & 0 \\ \vdots & \vdots & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots \\ 0 & 0 & 0 & 0 & 0 & \cdots & -1 & 3 \\ -1 & 0 & 0 & 0 & 0 & \cdots & 0 & -1 \\ \end{matrix} \right|_{(n - 1)\times (n - 1)} \]

注意第一个行列式的形式已经很优美了,并且后两项行列式貌似可以化成第一个行列式的形式,可以设 \(f_{n}\) 表示形如第一项的 \(n\) 阶行列式,然后将后两项行列式用 \(f\) 表示,之后再考虑算 \(f\) 的事情。

注意到后两个行列式均有列向量 \(\left[ \begin{matrix} -1 \\ 0 \\ 0 \\ \vdots \\ 0 \\ -1 \\ \end{matrix} \right]\),因此对这一列进行拉普拉斯展开比较科学。

化开中间的行列式:

\[\begin{aligned} \dots &= - \left| \begin{matrix} 3 & -1 & 0 & \cdots & 0 & 0 & 0 \\ -1 & 3 & -1 & \cdots & 0 & 0 & 0 \\ \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ 0 & 0 & 0 & \cdots & -1 & 3 & -1 \\ 0 & 0 & 0 & \cdots & 0 & -1 & 3 \\ \end{matrix} \right|_{(n - 2)\times (n - 2)} + (-1)^{n + 1}\left| \begin{matrix} -1 & 0 & 0 & \cdots & 0 & 0 & 0 \\ 3 & -1 & 0 & \cdots & 0 & 0 & 0 \\ -1 & 3 & -1 & \cdots & 0 & 0 & 0 \\ \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ 0 & 0 & 0 & \cdots & -1 & 3 & -1 \\ \end{matrix} \right|_{(n - 2)\times (n - 2)} \\ &= -f_{n - 2} - 1 \\ \end{aligned} \]

(这里曾经有一个憨憨对三角矩阵进行拉普拉斯展开:|)

化开第三项行列式:

\[\begin{aligned} \dots &= (-1)^{n}\left( - \left| \begin{matrix} -1 & 3 & -1 & 0 & \cdots & 0 & 0 \\ 0 & -1 & 3 & -1 & \cdots & 0 & 0 \\ \vdots & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots \\ 0 & 0 & 0 & 0 & \cdots & -1 & 3 \\ 0 & 0 & 0 & 0 & \cdots & 0 & -1 \\ \end{matrix} \right|_{(n - 2)\times (n - 2)} +(-1)^{n + 1} \left| \begin{matrix} 3 & -1 & 0 & 0 & \cdots & 0 & 0 \\ -1 & 3 & -1 & 0 & \cdots & 0 & 0 \\ 0 & -1 & 3 & -1 & \cdots & 0 & 0 \\ \vdots & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots \\ 0 & 0 & 0 & 0 & \cdots & -1 & 3 \\ \end{matrix} \right|_{(n - 2)\times (n - 2)} \right) \\ &= -1 - f_{n - 2} \end{aligned} \]

整理一下,有:

\[\begin{aligned} ans_{n} = 3f_{n - 1} - 2f_{n - 2} - 2 \\ \end{aligned} \]

下面类似地求 \(f_{n}\)

\[\begin{aligned} f_{n} &= \left| \begin{matrix} 3 & -1 & 0 & 0 & \cdots & 0 & 0 & 0 \\ -1 & 3 & -1 & 0 & \cdots & 0 & 0 & 0 \\ 0 & -1 & 3 & -1 & \cdots & 0 & 0 & 0 \\ \vdots & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ 0 & 0 & 0 & 0 & \cdots & -1 & 3 & -1 \\ 0 & 0 & 0 & 0 & \cdots & 0 & -1 & 3 \\ \end{matrix} \right|_{n\times n} \\ &= 3\left| \begin{matrix} 3 & -1 & 0 & \cdots & 0 & 0 & 0 \\ -1 & 3 & -1 & \cdots & 0 & 0 & 0 \\ \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ 0 & 0 & 0 & \cdots & -1 & 3 & -1 \\ 0 & 0 & 0 & \cdots & 0 & -1 & 3 \\ \end{matrix} \right|_{(n - 1)\times (n - 1)} + \left| \begin{matrix} -1 & -1 & 0 & \cdots & 0 & 0 & 0 \\ 0 & 3 & -1 & \cdots & 0 & 0 & 0 \\ \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ 0 & 0 & 0 & \cdots & -1 & 3 & -1 \\ 0 & 0 & 0 & \cdots & 0 & -1 & 3 \\ \end{matrix} \right|_{(n - 1)\times (n - 1)} \\ &= 3f_{n - 1} - \left| \begin{matrix} 3 & -1 & \cdots & 0 & 0 & 0 \\ \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ 0 & 0 & \cdots & -1 & 3 & -1 \\ 0 & 0 & \cdots & 0 & -1 & 3 \\ \end{matrix} \right|_{(n - 2)\times (n - 2)} \\ &= 3f_{n - 1} - f_{n - 2} \\ \end{aligned} \]

于是 \(f\) 可以递推计算,可以手算出边界 \(f_{1} = 3\)\(f_{2} = \left| \begin{matrix} 3 & -1 \\ -1 & 3 \\ \end{matrix} \right| = 8\)

剩下的就是高精度处理了。

特判 \(ans_{1} = 1\)\(ans_{2} = 5\)

LGV引理

基础知识

还是抄的 oi-wiki 的。

Lindström–Gessel–Viennot lemma,即 LGV 引理,可以用来处理 有向无环图 上不相交路径计数等问题。

记:

\(w(P)\) 表示 \(P\) 这条路径上所有边的边权之积(路径计数时则将边权均设为 \(1\));

\(e(u, v)\) 表示 \(u\)\(v\) 的所有路径 \(P\)\(w(P)\) 之和,即 \(e(u, v) = \sum\limits_{P : u \to v}w(P)\)

起点集合 \(A\) 是有向无环图点集的一个子集,大小为 \(n\)

终点集合 \(B\) 也是有向无环图点集的一个子集,大小也为 \(n\)

一组 \(A \to B\) 的不相交路径构成的集合 \(S\)\(S_{i}\) 表示 \(A_{i} \to B_{p(S)_{i}}\) 的路径(\(p(S)\) 是一个关于 \(S\) 的排列),且对于任意 \(i \neq j\)\(S_{i}\)\(S_{j}\) 没有公共顶点。

\(\mathrm{sgn}(p(S))\) 表示排列 \(p(S)\) 的逆序对数。

LGV 引理

\[\begin{aligned} \det(M) = \sum\limits_{S : A \to B}(-1)^{\mathrm{sgn}(p(S))}\prod\limits_{i = 1}^{n}w(S_{i}) \\ \end{aligned} \]

其中:

\[M = \left[ \begin{matrix} e(A_{1}, B_{1}) & e(A_{1}, B_{2}) & \cdots & e(A_{1}, B_{n}) \\ e(A_{2}, B_{1}) & e(A_{2}, B_{2}) & \cdots & e(A_{2}, B_{n}) \\ \vdots & \vdots & \ddots & \vdots \\ e(A_{n}, B_{1}) & e(A_{n}, B_{2}) & \cdots & e(A_{n}, B_{n}) \\ \end{matrix} \right] \]

P6657 【模板】LGV 引理

Problem

有一个 \(n\times n\) 的棋盘,左下角为 \((1,1)\),右上角为 \((n,n)\),若一个棋子在点 \((x,y)\),那么走一步只能走到 \((x+1,y)\)\((x,y+1)\)

现在有 \(m\) 个棋子,第 \(i\) 个棋子一开始放在 \((a_i,1)\),最终要走到 \((b_i,n)\)。问有多少种方案,使得每个棋子都能从起点走到终点,且对于所有棋子,走过路径上的点互不相交。输出方案数 \(\bmod\ 998244353\) 的值。

\(2 \le n \le 10^6\)\(1 \le m \le 100\)\(1 \le a_{1} \le a_{2} \le \dots \le a_{m} \le n\)\(1 \le b_{1} \le b_{2} \le \dots \le b_{m}\)

Solution

由于起点 \(x\) 坐标单增,终点 \(x\) 坐标单增,因此一个合法方案(即不相交路径组)对应的排列一定是 \(\{ 1, 2, \dots, m \}\),也即上面的 \(\det(M)\) 就是答案。

若一个起点 \(A_{i}\) 的坐标为 \((x_{a}, 1)\),一个终点 \(B_{j}\) 的坐标为 \((x_{b}, n)\),则 \(M\) 中的第 \(i\) 行第 \(j\) 列为 \(e(A_{i}, B_{j}) = \binom{x_{b} - x_{a} + n - 1}{n - 1}\)

此题启发我们,在某些题目中,由于不相交路径的特殊性,$\sum\limits_{S : A \to B}(-1)^{\mathrm{sgn}(p(S))}\prod\limits_{i = 1}^{n}w(S_{i}) $ 中只会包含特殊情形的 \(S\)(比如 \(p(S)\) 只能为升序),能恰好与题目所求相吻合,使得可以用 \(\det(M)\) 计算答案。

CF348D Turtles

Problem

给定一张 \(n\)\(m\) 列的网格图,图中的有些格子上面有障碍物,但保证 \((1,1)\)\((n,m)\) 上面都没有障碍物。在 \((1,1)\) 处有两只乌龟,都想要去 \((n,m)\)。乌龟每次都可以向下或者向右走一格,前提是格子上没有任何障碍物。要求两只乌龟在前往 \((n,m)\) 的路途中不可以相遇,即除了起点和终点,他们的路径没有其他公共点。求出从起点到终点的不同路径对数。答案对 \(10^9+7\) 取模。

注:\((route_a,route_b)\)\((route_b,route_a)\) 被视为同一对路径。

\(2 \le n, m \le 3000\)

Solution

这里需要自己构造起点集合 \(A = \{ (1, 2), (2, 1) \}\) 和终点集合 \(B = \{ (n - 1, m), (n, m - 1) \}\)

然后和上一题一样,都是只要方案合法则逆序对为 \(0\),因此可以利用 \(\det(M)\) 表示答案,然后用组合数算出 \(M\) 矩阵直接求值。由于此题是二阶行列式,所以可以直接由公式 \(\left[ \begin{matrix} a & b \\ c & d \\ \end{matrix} \right] = ad - bc\) 算出答案。这个式子不难由容斥原理进行理解。

P7736 [NOI2021] 路径交点

Problem

给定一个有向无环图,图可以分为 \(k\) 层,第 \(i\) 层的点数为 \(n_{i}\),且保证 \(n_{1} = n_{k}\),第 \(i\) 层的点只会连向第 \(i + 1\) 层的点。现在你要从图中选出 \(n_{1} = n_{k}\) 条路径,每条路径以第 \(1\) 层顶点作为起点,以第 \(k\) 层顶点作为终点,并 要求图中的每个点至多出现在一条路径中

求产生偶数个交点的路径方案数减去产生奇数个交点的路径方案数对 \(998244353\) 取模的结果。

\(2 \le k \le 100\)\(2 \le n_{1} \le 100\)\(n_{1} \le n_{i} \le 2n_{1}\)

Solution

双倍经验:CF167E Wizards and Bets

此题基本上还原了整个引理的内容。

起点集合为第一层点 \(A_{1}, A_{2}, \dots, A_{n_{1}}\),终点集合为最后一层点 \(B_{1}, B_{2}, \dots, B_{n_{k}}\)。考察两条路径 \(A_{i} \to B_{p_{i}}\)\(A_{j} \to B_{p_{j}}\) 的交点个数的奇偶性(\(i < j\)),发现当 \(p_{i} > p_{j}\) 时交点个数为奇数,否则为偶数(相交一次会改变一次顺序,相交两次会改变回来,以此类推)。也即,当存在一个逆序对时,说明对应的两条路径的交点个数为奇数,进一步可知总交点个数的奇偶性等价于排列 \(p\) 的奇偶性。

题目所求与 \(\sum\limits_{S : A \to B}(-1)^{\mathrm{sgn}(p(S))}\prod\limits_{i = 1}^{n}w(S_{i}) = \det(M)\) 的形式完全一致,\(M\) 的各项可以直接对反图跑 BFS 求出。

BEST定理

赶复习进度,先搁了。

基础知识

了解一下名字是怎么来的:"The name is an acronym of the names of people who discovered it: de Bruijn, van Aardenne-Ehrenfest, Smith and Tutte."

还是抄的 oi-wiki 的。

\(G = (V, E)\)有向欧拉图,则 \(G\) 的不同欧拉回路总数为:

\[\begin{aligned} t^{root}(G, k)\prod\limits_{u \in V}(d_{u} - 1)! \text{ for all } k \\ \end{aligned} \]

线性基入门

以下若未特殊说明,均指数域 \(F = \{ 0, 1 \}\) 上的向量空间的基组,即OI 中常见的 异或线性基

求基原理同高斯消元,故不作理论解释。线性基的若干性质在后文以例题的形式给出。

下称 自由位 表示基组每个元素的最高位。

P3812 【模板】线性基

Problem

求一堆数的异或最大值。

Solution

下面展示了异或线性基的实现。

其中 \(b_{i}\) 表示以第 \(i\) 位为最高位的一个基元素。

线性基求异或最小值:若不存在异或值为 \(0\),则答案必为线性基中最小的元素,正确性显然。

线性基求异或最大值:从高位到低位枚举 \(b\),能使答案变大则异或上当前位对应的 \(b\)。正确性可以考虑当前答案在当前位上为 \(0 / 1\),发现异或上 \(b\) 后对答案最终的影响与异或上 \(b\) 后对当前答案的直接影响是相同的,因此可以使用该贪心策略。

struct Linear_Base
{
	LL b[N];
	int insert(LL x)
	{
		for(int i = N - 1; i >= 0; --i)
			if(x & (1LL << i))
			{
				if(b[i]) x ^= b[i];
				else return b[i] = x, 1;
			}
		return 0;
	}
	LL query_max()
	{
		LL res = 0;
		for(int i = N - 1; i >= 0; --i)
			chkmx(res, res ^ b[i]);
		return res;
	}
}L; 

时间复杂度 \(O(n\log{V})\)

P3857 [TJOI2008] 彩灯

Problem

给定若干数和 \(0\),求它们能异或出的数的数量。

Solution

记线性基为 \(S\)。如果不保证能异或出 \(0\),则答案为 \(2^{|S|} - 1\),否则为 \(2^{|S|}\)。此题一定为后者。

正确性显然,线性基里的元素线性无关,只需确定它们的组合系数(\(0 / 1\))即可。

P4570 [BJWC2011] 元素

Problem

\(n\) 个元素,每个元素有个序号和一个价值,一个元素可以选择当且仅当其序号与已选元素序号的异或和不为 \(0\),求你可选择的元素价值和的最大值。

Solution

将元素按价值从大到小排序后能插就插,线性基内的元素对应的价值和即为答案。

因为不在线性基内的元素想要进入基内就必须在线性基内拿一个出来换,由于我们已经按价值排序且采用能插就插的策略,因此换进去一定不优。

P4869 albus就是要第一个出场

Problem

将一个大小为 \(n\) 集合的所有子集异或和排序(定义空集的异或和为 \(0\);结果不去重),问某个数 \(x\) 的排名。

Solution

记线性基为 \(S\)。由于允许空集,则线性基能表示出的数有 \(2^{|S|}\) 个,且次数均为 \(2^{n - |S|}\)(考虑线性基外的 \(2^{n - |S|}\) 个异或和,每一个都能在线性基内唯一表示)。

然后只需求出在 \(\mathrm{span(S)} \cup \{ 0 \}\) 中比 \(x\) 小的数的个数 \(w\),答案即为 \(2^{n - |S|}w + 1\)

现在的问题是如何求出 \(w\)

我们先思考一个类似的问题:求线性基张成空间中的第 \(k\) 小元素。其实把自由位按顺序排列,\(k\) 在自由位上的二进制表示对应的线性组合即为答案。

比如如果一组线性基为 100100100000010,则其张成空间中的第 \(3\) 小元素(011)相当于使得右起第 \(1\) 位、第 \(3\) 位为 \(1\)、第 \(4\) 位为 \(0\) 的线性组合(从 \(0\) 开始),第 \(5\) 小元素(101)相当于使得右起第 \(1\) 位为 \(1\)、第 \(3\) 位为 \(0\)、第 \(4\) 位为 \(1\) 的线性组合(从 \(0\) 开始)。

正确性可以从最高自由位开始考虑,其为 \(1\) 的线性组合代表的元素一定比其为 \(0\) 的线性组合代表的元素值更大,以此类推,发现这就等价于排名在自由位上的二进制表示,容易证明自由位的二进制状态唯一对应一个线性组合。但注意,自由位的二进制状态并不等价于组合系数的二进制表示!后者需要按最高位从大到小的顺序确定基元素的组合系数。

然后再用类似地思想去解决原问题。这里我们的解决思路是:从高位向低位尽可能用线性基中的元素表示出 \(x\)

\(x\) 在二进制下从高位向低位考虑,同时维护值 \(y\) 表示考虑 \(x\) 的高若干位时选取线性基中的某些元素进行异或的结果。初始化 \(y = 0\)

若在某一非自由位上 \(x\)\(y\) 不同,则不用再考虑之后的自由位,因为之后的自由位一定不会影响到当前位的大小判定。比较该位 \(x, y\) 的大小,如果在该位上 \(x\)\(1\)\(y\)\(0\),则将 \(w\) 加上 \(2^{c}\)\(c\) 表示当前位及更低位上的自由位个数),然后返回答案。否则直接返回答案。

若当前位为自由位,考察 \(x\)\(y\) 在该位上是否相同,如果不同就让 \(y\) 异或该位上的基元素。如果 \(x\) 在该位上为 \(1\),使 \(w\) 加上 \(2^{c - 1}\)\(c\) 表示当前位及更低位上的自由位个数,\(2^{c - 1}\) 相当于加上了钦定该位为 \(0\) 的元素个数)。

这样就可以得到 \(w\) 了。

值得一提的是,如果将所有位都遍历完了,但都没有退出,则最后一定有 \(x = y\),且 \(y\) 是线性基的一个自由组合。

// Sea, You & Me
const int F = 31;
int qwq[F];
struct Linear_Base
{
	int b[F];
	int insert(int x)
	{
		for(int i = F - 1; i >= 0; --i)
			if(x & (1 << i))
			{
				if(b[i]) x ^= b[i];
				else return b[i] = x, 1;
			}
		return 0;
	}	
	int get(int x) // the num < x (including 0)
	{
		int sum[F];
		sum[0] = (b[0] != 0);
		for(int i = 1; i < F; ++i)
			sum[i] = sum[i - 1] + (b[i] != 0);
		int res = 0, y = 0;
		for(int i = F - 1; i >= 0; --i)
		{
			int _x = (x >> i) & 1;
			int _y = (y >> i) & 1;
			if(!b[i])
			{
				if(_x ^ _y)
				{
					if(_x > _y)
						res += qwq[sum[i]];
					break;	
				}	
			} 
			else
			{
				if(_x ^ _y) y ^= b[i];
				if(_x == 1) res += qwq[sum[i] - 1];
			}
		}
		return res;
	}
}L;
int n;
int main()
{
	qwq[0] = 1;
	for(int i = 1; i < F; ++i)
		qwq[i] = mul(qwq[i - 1], 2);
	read(n);
	int cnt = 0;
	for(int i = 1; i <= n; ++i)
	{
		int x; read(x);
		cnt += L.insert(x);		
	}
	int x; read(x);
	int res = L.get(x);
	res = inc(mul(res % MOD, qwqmi(2, n - cnt)), 1);
	printf("%d\n", res);
	return 0;
}

P4151 [WC2011] 最大XOR和路径

Problem

考虑一个边权为非负整数的无向连通图,节点编号为 \(1\)\(n\),试求出一条从 \(1\) 号节点到 \(n\) 号节点的路径,使得路径上经过的边的权值的 XOR 和最大。

路径可以重复经过某些点或边,当一条边在路径中出现了多次时,其权值在计算 XOR 和时也要被计算相应多的次数。

\(n \le 5 \times 10^4\)\(m \le 10^{5}\)

Solution

这篇题解 讲得很好。

可能的异或和为:图上所有环的异或和构成的线性基张成出的异或和与任意一条 \(1 \to n\) 简单路径的异或和

线性基内元素与给定元素的异或最大值可以类似线性基异或最大值的求解方式,从高位到低位枚举基元素,能使答案更大就异或上当前基元素即可。

// Sea, You & Me
const int N = 5e4 + 5;
const LL V = 60;
struct Linear_Base
{
	LL b[V];
	int cnt;
	void clear()
	{
		cnt = 0;
		for(int i = 0; i < V; ++i)
			b[i] = 0;
	}
	void insert(LL x)
	{
		for(int i = V - 1; i >= 0; --i)
			if(x & (1LL << i))
			{
				if(b[i]) x ^= b[i];
				else return b[i] = x, ++cnt, void();
			}
	}
	LL query_max(LL res)
	{
		for(int i = V - 1; i >= 0; --i)
			chkmx(res, res ^ b[i]);
		return res;
	}
}L;
int n, m;
vector<pair<int, LL> > G[N];
int vis[N]; LL dis[N];
void dfs(int u, LL s)
{
	dis[u] = s;
	vis[u] = 1;
	for(auto nxt : G[u])
	{
		int v = nxt.fi;
		LL w = nxt.se;
		if(!vis[v]) dfs(v, s ^ w);
		else L.insert(s ^ w ^ dis[v]);
	}
}
int main()
{
	read(n), read(m);
	for(int i = 1; i <= m; ++i)
	{
		int x, y; LL z;
		read(x), read(y), read(z);
		G[x].EB(MP(y, z));
		G[y].EB(MP(x, z));
	}
	dfs(1, 0);
	printf("%lld\n", L.query_max(dis[n]));
	return 0;
}

双倍经验:CF845G Shortest Path Problem?

改成求异或最小值即可,原理相同:

int query_min(int res)
{
    for(int i = V - 1; i >= 0; --i)
        chkmn(res, res ^ b[i]);
    return res;
}

线性基合并

其实是一个很简单的技巧,就是把一个线性基内的元素暴力插入到另一个线性基中,单次合并的时间复杂度为 \(O(\log^{2}{V})\)

P4839 P 哥的桶

Problem

\(n\) 个集合编号为 \(1 \sim n\)\(m\) 次操作,要求支持:

  • 向一个集合中插入一个元素。
  • 查询集合 \(l \sim r\) 并集的异或最大值。

\(1 \le n, m \le 5 \times 10^4\)

Solution

拿线段树来合并信息即可,插入时间复杂度为 \(O(m\log{n}\log{V})\),查询时间复杂度为 \(O(m\log{n}\log^{2}{V})\)

一点优化:当一个线性基满了就可以不用再插入了,这可以从 4.8s 降到 0.8s。

// Sea, You & Me
const int N = 5e4 + 5;
const int V = 31;
struct Linear_Base
{
	int b[V], cnt;
	void clear()
	{
		cnt = 0;
		memset(b, 0, sizeof(b));
	}
	void insert(int x)
	{
		if(cnt == V) return; // optimization
		for(int i = V - 1; i >= 0; --i)
			if((x >> i) & 1)
			{
				if(b[i]) x ^= b[i];
				else return b[i] = x, ++cnt, void();
			}
	}
	int query_max()
	{
		int res = 0;
		for(int i = V - 1; i >= 0; --i)
			chkmx(res, res ^ b[i]);
		return res;
	}
	friend Linear_Base operator + (Linear_Base A, Linear_Base B)
	{
		for(int i = 0; i < V; ++i)
			if(B.b[i]) A.insert(B.b[i]);
		return A;
	}
};

P3292 [SCOI2016] 幸运数字

Problem

给出一棵树,求树上两点间简单路径上的点的异或最大值。

\(n \le 2 \times 10^4\)\(Q \le 2 \times 10^5\)

Solution

可以沿用上一题的思路,再套一个树链剖分,可以做到四只老哥,非常不优秀。

既然此题没有修改,我们可以在预处理上下功夫。

我们可以类似 LCA 的思想,对每个节点求出向上跳的倍增线性基,然后在查询时进行 \(O(\log{n})\) 次合并。

这样可以做到时间复杂度 \(O((n + Q)\log{n}\log^{2}{V})\),如果常数小应该是可以卡过去的。

但存在更优秀的做法:注意线性基的合并运算具有 可重复贡献性,在这里体现为一个相同的元素多次插入线性基,不会影响线性基最终张成的空间。

于是可以像 ST 表那样查询,只需合并常数个线性基,可以做到时间复杂度 \(O(n\log{n}\log^{2}{V} + Q(\log{n} + \log^{2}{V}))\)

空间复杂度均为 \(O(n\log{n}\log{V})\)

CF1100F Ivan and Burgers

Problem

给定一个长为 \(n\) 的序列,\(Q\) 查询区间异或最大值。

\(1 \le n, Q \le 5 \times 10^{5}\)

Solution 1

幸运数字的序列版本,但数据范围更大了,三只老哥很难冲得过去。

思考线段树和 ST 表的瓶颈,都是在于过多次数的线性基合并,所以我们的一种思路是优化线性基合并的次数。

这里考虑离线分治(整体二分)。

对于当前分治中心 \(mid\),处理出其向左的后缀线性基和向右的前缀线性基,然后对所有跨越 \(mid\) 的询问进行单次线性基合并。对于没有跨越 \(mid\) 的询问,递归下去解决。处理前后缀线性基的总时间复杂度为 \(O(n\log{n}\log{V})\),处理询问的时间复杂度为 \(O(Q\log^{2}{V})\)

code CF1100F - divide

Solution 2

有一个在线的两只老哥做法,没有用到线性基合并。

该做法在普通的异或线性基上,多记录了 取得基元素的位置

将原序列元素从左到右进行插入,得到 \(n\) 个前缀线性基,在插入过程中,不断将当前位置的元素在线性基中进行替换,使得取得基元素的位置尽量靠后。

查询区间 \([l, r]\) 时,使用第 \(r\) 个版本的线性基,从高位到低位考虑基元素,如果取得当前基元素的位置 \(\ge l\) 且异或上能使答案更优,则异或上该元素。

该贪心过程的正确性是:如果当前元素与线性基线性无关,则会增加线性基维数,不管如何插入,线性基的基元素位置集合都是一样的。

否则,此过程等价于枚举用当前元素替换掉某些基元素(能被替换掉的基元素集合 \(S\) 是确定的,可以证明插入过程可以考虑到集合 \(S\) 中的所有基元素),使得线性基张成的空间不变。这无疑可以使位置换到最优。

如果硬要解释《可以证明》,那我就开始胡言乱语了:

按最高位从大到小考虑基元素 \(\vec{v_{1}}, \vec{v_{2}}, \dots, \vec{v_{m}}\),记当前元素为 \(w\),并且钦定 \(w\)\((\vec{v_{1}}, \vec{v_{2}}, \dots, \vec{v_{m}})\) 线性相关。

如果一个基元素 \(\vec{v_{i}}\) 可以被 \(w\) 换出,等价于存在一组组合系数 \(a_{1}, \dots, a_{i - 1}, a_{i + 1}, \dots, a_{m}\),使得 \(\vec{v_{i}} = w \oplus \bigoplus\limits_{1 \le j \le m, i \neq j}a_{j}\vec{v_{j}}\)

先考虑 \(\vec{v_{1}}\),如果 \(w\)\(\vec{v_{1}}\) 对应的最高位为 \(1\),则 \(w\) 一定可以换出 \(\vec{v_{1}}\)。为什么?因为 \((\vec{v_{1}}, \vec{v_{2}}, \dots, \vec{v_{m}})\) 线性无关,把每个基元素对应一个维度,则 \(\vec{v_{1}}\) 对应的必然是最高位。将 \(\vec{v_{1}}\) 删除后,只会影响到最高位维度的消失;此时加入 \(w\),必然可以重新扩展最高位所在的维度,也只会影响到最高位所在的维度。反之,如果 \(w\)\(\vec{v_{1}}\) 对应的最高位为 \(0\),则 \(\vec{v_{1}}\) 的存在是必要的,不能被换出。

假设 \(\vec{v_{1}}\) 可以被 \(w\) 换出,如果要尝试表示出接下来的 \(\vec{v_{i}}\),则 \(a_{1}\) 必然为 \(1\),否则消不掉最高位,那么接下来一定不可能表示出 \(\vec{v_{i}}(i = 2, \dots, m)\)。但在实现上的解释略有不同:将线性基中的 \(\vec{v_{1}}\) 换成 \(w\),并把 \(w \oplus \vec{v_{1}}\) 作为接下来要插入的元素。这步操作在判定式上可以理解为:把 \(a_{1}\) 设置为 \(0\),并把 \(w\) 替换为 \(w \oplus \vec{v_{i}}\)

最终,所有能被 \(w\) 换出的基元素,都会被枚举一遍。选择其中位置最小的那个基元素换出即可,这也的确是最优的策略。

// Sea, You & Me
const int N = 5e5 + 5;
const int V = 20;
struct Linear_Base
{
	int b[V], pos[V];
	void clear()
	{
		for(int i = 0; i < V; ++i)
			b[i] = pos[i] = 0;
	}
	void insert(int x, int p)
	{
		for(int i = V - 1; i >= 0; --i)
			if(x & (1 << i))
			{
				if(!b[i]) return b[i] = x, pos[i] = p, void();
				else if(pos[i] < p) swap(pos[i], p), swap(b[i], x);
				x ^= b[i];
			}
	}
	int query_max(int l)
	{
		int res = 0;
		for(int i = V - 1; i >= 0; --i)
			if(b[i] && pos[i] >= l)
				chkmx(res, res ^ b[i]);
		return res;
	}
}LB[N];
int n, Q;
int main()
{
	read(n);
	LB[0].clear();
	for(int i = 1; i <= n; ++i)
	{
		LB[i] = LB[i - 1];
		int x; read(x);
		LB[i].insert(x, i);
	}
	read(Q);
	for(int cas = 1; cas <= Q; ++cas)
	{
		int l, r;
		read(l), read(r);
		printf("%d\n", LB[r].query_max(l));
	}
	return 0;
}

code CF1100F - on-line

P5607 [Ynoi2013] 无力回天 NOI2017

Problem

给你一个长度为 n 的整数序列 \(a_1\), \(a_2\), \(\ldots\), \(a_n\) ,你需要实现以下两种操作,每个操作都可以用四个整数 \(opt\;l\;r\;v\) 来表示:

\(opt=1\) 时,代表把一个区间 \([l,r]\) 内的所有数都 xor 上 \(v\)

\(opt=2\) 时, 查询一个区间 \([l,r]\) 内选任意个数(包括 \(0\) 个)数 xor 起来,这个值与 \(v\) 的最大 xor 和是多少。

\(1 \le n, Q \le 5 \times 10^{4}\)

Solution

区间查询线性基,不必一定要用初始元素进行插入,只要最后张成的空间是不变的,我们可以用其它更简易的方式进行线性基的构造。

这里考虑使用 差分 构造。如果使用原数组构造线性基,则区间修改带来的影响是很难实现的(其实打一下懒标记大概也是能做的?)。但差分之后就会容易得多。

具体地,记 \(d_{i} = a_{i} \oplus a_{i - 1}\),则查询区间 \([l, r]\) 的线性基等价于查询 \(a_{l}, b_{l + 1}, b_{l + 2}, \dots, b_{r}\) 构造出的线性基。

对于 \(a_{l}\),相当于对原数组支持区间异或,单点查询(或者直接对 \(b\) 数组单点修改,查询前缀和),这是容易单 \(\log\) 解决的。

对于 \(b\),区间 \([l, r]\) 异或 \(v\) 相当于 \(b_{l}\)\(b_{r + 1}\) 异或上 \(v\),可以单次 \(O(\log{n}\log{V})\) 修改 \(b\) 数组构造出的线性基。

时间复杂度瓶颈依然是查询的三只老哥。注意特判一下 \(l = r\) 的情况。

一道思想完全一致的题目:CF587E Duff as a Queen

线性代数在 OI 中的其它应用

P7016 [CERC2013] Captain Obvious and the Rabbit-Man

Problem

众所周知,斐波那契数列的公式如下:

\[F_i=\begin{cases}1&i=1\\2&i=2\\F_{i-1}+F_{i-2}&i\geqslant 3\end{cases} \]

定义 \(p_i=\sum\limits_{j=1}^ka_j\times F_j^i\)。现在给定 \(k,m\) 以及 \(\{p_i\}_{i=1}^k\),请求出 \(p_{k+1}\bmod m\)

\(1 \le k \le 4000\)\(3 \le m \le 10^9\)\(m \in primes\)

Solution

相当于给出下面这个以 \(a\)\(p_{k + 1}\) 为未知数的方程 \(A\vec{x} = b\)

\[\begin{aligned} \left[ \begin{matrix} F_{1}^{1} & F_{2}^{1} & \cdots & F_{k}^{1} \\ F_{1}^{2} & F_{2}^{2} & \cdots & F_{k}^{2} \\ \vdots & \vdots & \ddots & \vdots \\ F_{1}^{k + 1} & F_{2}^{k + 1} & \cdots & F_{k}^{k + 1} \\ \end{matrix} \right] \left[ \begin{matrix} a_{1} \\ a_{2} \\ \vdots \\ a_{k} \\ \end{matrix} \right] = \left[ \begin{matrix} p_{1} \\ p_{2} \\ \vdots \\ p_{k + 1} \\ \end{matrix} \right] \end{aligned} \]

可以先取 \(A\)\(b\) 的前 \(k\) 行解方程,将 \(\vec{x} = \left[ \begin{matrix} a_{1} \\ a_{2} \\ \vdots \\ a_{k} \\ \end{matrix} \right]\) 解出,然后再代回原式即可求出 \(p_{k + 1}\)

\(A\) 的前 \(k\) 行类似于范德蒙德矩阵,同样可以采用伴随矩阵求逆的方法进行求解。

posted @ 2023-12-22 19:21  Schucking_Sattin  阅读(32)  评论(0编辑  收藏  举报