矩阵学习笔记

· · 算法·理论

矩阵学习笔记

基本知识

定义

由 m \times n 个数 a_{i,j} 排成的 m 行 n 列的数表称为 m 行 n 列的矩阵,简称 m \times n 矩阵。记作(以三乘三矩阵为例):

A=\begin{vmatrix} a_{11}& a_{12} & a_{13}\\ a_{21}& a_{22} & a_{23}\\ a_{31} & a_{32} & a_{33} \end{vmatrix}

矩阵乘法

对于两个 n \times m 与 m \times k 矩阵 A,B 定义它们相乘的结果是一个 n \times k 矩阵 C,且有 c_{i,j}=\sum_{x=1}^m a_{i,x}b_{x,j}。

对于一个 n\times n 的矩阵与一个 n\times 1 的矩阵的矩阵乘法,可以视作对于 n \times 1 矩阵中元素的一种变换,例如:

\begin{vmatrix} 0 & 1\\ 1 &1 \end{vmatrix} \begin{vmatrix} f_{i-1}\\ f_i \end{vmatrix} = \begin{vmatrix} f_i \\ f_{i+1} \end{vmatrix}

这也是斐波那契数列的递推,基于结合律可以使用快速幂加速。

矩阵乘法具有结合律,但不具有交换律,读者自证不难。

基矩阵/单位矩阵

定义:与任意矩阵基矩阵相乘后不变。

形状:(显然为 n \times n 矩阵)

\forall i \in [1,n],a_{i,i}=1 \\ \forall i \ne j \in [1,n],a_{i,j}=0

热身

T1 P1939 矩阵加速(数列)

显然的,我们有:

\begin{vmatrix} 0 & 1 &0 \\ 0 & 0 &1 \\ 1 & 0 & 1 \end{vmatrix} \begin{vmatrix} f_{i-2} \\ f_{i-1} \\ f_i \end{vmatrix} = \begin{vmatrix} f_{i-1} \\ f_i \\ f_{i+1} \end{vmatrix}

直接快速幂加速即可,复杂度 \mathcal O(t\log n)。

矩阵变形-“+ max”矩阵

其余定义同常规矩阵,乘法的定义改为 c_{i,j}=\max_{x=1}^m a_{i,x}+b_{x,j}。

显然的,此时的矩阵乘法具有结合律,但不具有交换律。

“+ max”矩阵的基矩阵为:

\forall i \in [1,n],a_{i,i}=0 \\ \forall i \ne j \in [1,n],a_{i,j}=-\infty

线段树维护矩阵

由于矩阵乘法有一种“变幻”的性质,所以可以将线段树每个节点上拥有的数值视作一个 n \times 1 的向量(可以理解为一种特殊的只有 1 列的矩阵),每次操作就是给这个向量乘上一个变幻矩阵,用 lazy-tag 维护。下面是一些例题。

T2 【模板】线段树 2

考虑给每个线段树维护 sum 对于每种操作,有(上乘下加):

sum \leftarrow sum*v \\ sum \leftarrow sum+(v*len)

因此还要维护一个辅助变量 len 在向量中。

实现上,对于每个节点,维护向量:

\begin{vmatrix} sum \\ len \end{vmatrix}

对于每次加操作,给对应节点的向量与懒标记乘上矩阵:

\begin{vmatrix} 1 & x \\ 0 & 1 \end{vmatrix}

容易发现这是对的。

对于每次乘操作,给对应节点的向量与懒标记乘上矩阵:

\begin{vmatrix} x & 0 \\ 0 & 1 \end{vmatrix}

下传时将每个节点的懒标记乘到子节点的向量与懒标记上,然后将懒标记重置为单位矩阵即可。

T3 [NOIP2022] 比赛

标记定义

$maxb(l,r)$ 数列b在[l,r]内的最大值。\ 在复杂度中默认$n$与$q$同阶。\ 单位矩阵:对角线全为1,其他地方全为0的矩阵。 #### 题目大意 给定数列$a_{1-n},b_{1-n}$,每次询问l,r,询问: $$ \sum ^r_{p=l} \sum ^r_{q=p} maxa(p,q)*maxb(p,q)$$ #### 转化 考虑离线,从 $1$ 至 $n$ 扫描 $r$ ,求解 $l$。 对于确定的r,我们先来考虑 $ \sum_{i=l}^rmaxa(i,r) * maxb(i,r)

如何求解

显然,这个式子可以用线段树维护,我们只需单调栈处理 a 与 b 最大值的变化,即可做到 O(nlogn) 的更新,但此时我们的查询却变成了 O(n^2logn),因此就需要用线段树去维护区间历史所有版本的和

如何维护历史和

我们在线段树的节点上新增一个 hisab_u 表示u节点上的历史和,但不难发现这个东西用常规手法很难维护(你也可以暴力维护一堆懒标记,但我懒得去讨论了),我们先来考虑一维的历史和如何维护

Part1 一维历史和

对于线段树的每个节点,维护向量val

\begin{vmatrix} hsum \\ sum \\ len \\ \end{vmatrix}

表示(从上到下)历史和,和,长度,区间修改时,记修改量为\Delta,即将区间的val乘上矩阵:

\begin{vmatrix} 1&0&0 \\ 0&1&\Delta \\ 0&0&1 \\ \end{vmatrix}

每轮更新结束后,将所有数的向量val乘上矩阵:

\begin{vmatrix} 1&1&0 \\ 0&1&0 \\ 0&0&1 \\ \end{vmatrix}

即可更新hsum。

这个东西可以开一颗线段树来维护每个节点的向量 val ,再记 tag_u 为节点 u 未下传的矩阵,下传时,将儿子的 tag 和 val 乘上父亲的 tag 然后将父亲的 tag 赋值为单位矩阵即可。

Part 2二维历史和

和上面一样,定义 val_u=

\begin{vmatrix} hsum \\ sumab \\ suma\\ sumb\\ len \\ \end{vmatrix}

更新a时,记变化量为 \Delta_a,直接将 val 乘上:

\begin{vmatrix} 1&0&0&0&0 \\ 0&1&0&\Delta_a&0 \\ 0&0&1&0&\Delta_a \\ 0&0&0&1&0 \\ 0&0&0&0&1 \end{vmatrix}

更新 b 时同理。

对于上传,每轮更新结束后,给 [1,n] 的 val 乘上:

\begin{vmatrix} 1&1&0&0&0 \\ 0&1&0&0&0 \\ 0&0&1&0&0 \\ 0&0&0&1&0 \\ 0&0&0&0&1 \end{vmatrix}

即可,维护方法同上,查询时直接查询 hsum[l,r], 复杂度为 \mathcal O(k^3nlogn) k=5。

常数优化

Part 1:基于矩阵性质的优化

注意到只有对角线上方有数,因此可以只维护对角线上方的数,这样可以将乘法次数从125优化到35次。

Part 2:线段树上的优化

注意到任何向量或矩阵乘上单位矩阵结果都不变,所以记 state_u 为u位置的 tag 是否是单位矩阵,只有 state_u=1 时 pushdown 时要去计算。

T4『STA - R4』冰红茶题解

标记含义

$\text{max1}$:节点对应的区间内最后连续和原味冰红茶瓶数最多的 bot 喝的冰红茶的瓶数。 $\text{max2}$:节点对应的区间内最后连续和热带风味冰红茶瓶数最多的 bot 喝的冰红茶的瓶数。 #### 思路 对于每个线段树上的点,记录向量 $$\text{val} =\begin{vmatrix}\text{sum}\\\text{max1}\\\text{max}2\\1\end{vmatrix}$$。 考虑怎么去更新。 ##### Part I 喝冰红茶 这里只讨论和原味冰红茶的情况,热带风味冰红茶也是同理。 对于一个区间的 bot,喝下 $\text{k}$ 瓶原味冰红茶后,$\text{max1} \leftarrow \text{max1}+\text{k},\text{max2} \leftarrow 0$,$\text{sum}$ 不变。 所以,我们可以直接给这个区间的 $\text{val}$ 乘上矩阵: $$ \begin{vmatrix} 1&0&0&0\\ 0&1&0&\text{k}\\ 0&0&0&0\\ 0&0&0&1 \end{vmatrix}$$ 这部分可以用线段树维护。 ##### Part II 击毁 bot 对于一个线段树节点,如果这个节点的 $\text{sum}$ 为 $0$ 或 $\max(\text{max1},\text{max2}) < \text{k}$,直接返回,否则递归处理左右儿子。 对于需要修改的叶子节点,将它的 $\text{val}$ 改为 $$\begin{pmatrix}0\\-\infty\\-\infty\\1\end{pmatrix}$$。 在 pushdown 时,不下传 $\text{sum}=0$ 区间的懒标记。 #### Part III 查询答案 最简单的一部分,直接输出 $\text{sum}_1$ 即可。 #### 复杂度分析 以下分析忽略矩阵乘法的常数。 对于喝冰红茶,显然是 $\mathcal O(n\log n)$的。 对于击毁 bot,我们发现每个 bot 只会被访问一次,因此对于每个 bot 的均摊复杂度是 $\mathcal O(\log n)$ 的,因此总复杂度为 $\mathcal O(n\log n)$。 综上,本题复杂度为 $\mathcal O(n\log n)$。 #### 常数优化 同上题。 ### T5 P4314 CPU 监控 维护区间历史最大值,考虑在向量中维护 $hismax$ 表示历史最大值,考虑怎么更新,自然的想到用 “+ max” 矩阵,然后就做完了。 ### T6 [THUSC 2017] 大魔法师 比较经典,留作习题答案略。 ### T7 [Ynoi2009] rprmq1 发现每次区间加可以转换成在 $l1$ 时刻给 $l2$ 到 $r2$ 加上 $x$,然后在 $l2+1$ 时刻给 $l2$ 到 $r2$ 减去 $x$,查询区间最大值等价于查询 $l1$ 到 $r1$ 时刻的 $l2$ 到 $r2$ 的历史最大值。 考虑线段树对时间轴分治,每个询问的 $l1$ 到 $r1$ 拆成若干个小区间。复杂度 $\mathcal O(n\log^2n)$。 ## 矩阵优化 dp ### T8【模板】动态 DP 首先有一个很 native 的线性 dp,记 $f_{u,0/1}$ 表示 $u$ 取或不取它的子树中的最大独立集。转移是简单的: $$ f_{u,0}=\sum_{v \in son_u}\max(f_{v,0},f_{v,1}) \\ f_{u,1}=\sum_{v \in son_u} f_{v,0} $$ 考虑如何优化转移,定义状态 $g_{u,0/1}$ 表示 $u$ 取或不取它的轻儿子子树的最大独立集。 有: $$ f_{u.0}=\max(f_{son_u,0},f_{son_u,1})+g_{u,0} \\ f_{u,1}=f_{son_u,0}+g_{u,1} \\ g_{u,0}=\sum_{v \in lightson_u}\max(f_{v,0},f_{v,1}) \\ g_{u,1}=\sum_{v \in lightson_u} f_{v,0} $$ 不难看出: $$ \begin{vmatrix} g_{u,0} & g_{u,0}\\ g_{u,1} & -\infty \end{vmatrix} \begin{vmatrix} f_{son_u,0} \\ f_{son_u,1} \end{vmatrix}= \begin{vmatrix} f_{u,0} \\ f_{u,1} \end{vmatrix} $$ 因此,每个点的 $f$ 可以由从它重链底部的 $g$ 矩阵一直乘到它的 $g$ 矩阵来获得(对于叶子节点,$f_{u,0/1}=g_{u,0/1}$)。此处使用线段树维护矩阵乘积,每个点与它重链底部的点在 dfn 序上是一段连续的区间。 此外,还能发现每个点的修改,只会更改 $\log$ 个点的 $g$。 因此考虑找到这些点,分别求出他们变化前后 $g$ 的变化,然后暴力维护即可。 ### T9 [NOIP 2018 提高组] 保卫王国 首先有一个显然的结论: 最小权覆盖=总权值-最大独立集。 考虑取最大独立集的补集即可证明。 对于强制选/不选,可以通过更换权值至 $\infty$ 或 $-\infty$ 来解决,然后就是动态 dp 板子题了。 ### T10 New Year and Old Subsequence 考虑暴力的 dp,记 $f_{i,0/1/2/3/4}$ 表示目前考虑到点 $i$,已有子序列 $\emptyset,2,20,201,2017$ 的最少删除次数(转移见代码)。 发现可以用矩阵转移,甚至是静态的,直接线段树维护 “+ max”矩阵即可,复杂度 $\mathcal O(n+qlogn)$。 有不懂的可以在讨论区问我。