矩阵乘法学习笔记

矩阵乘法

\(A\) 为一个 \(n\times m\) 的矩阵, \(B\) 为一个 \(m\times p\) 的矩阵,\(C\) 为矩阵 \(A\)\(B\) 的乘积,那么有

\[C_{i,j}=\sum_{k=1}^{m}A_{i,k}\times B_{k,j} \]

\(C\) 为一个 \(n\times p\) 的矩阵。显然,矩阵乘法的时间复杂度为 \(O(nmp)\)

struct mtx{
	long long t[N][N];
	int n,m;
}z;
inline mtx operator*(mtx a,mtx b)
{
	mtx c=z;c.n=a.n;c.m=b.m;
	for(int i=1;i<=a.n;i++)
		for(int j=1;j<=a.m;j++)
			for(int k=1;k<=a.m;k++)c.t[i][j]+=a.t[i][k]*b.t[k][j];
	return c;
}

矩阵快速幂

矩阵乘法不满足交换律,但满足结合律,所以我们可以用矩阵快速幂在 \(O(n^3 \log k)\) 的时间内求出 \(A^k\)

inline mtx quikp(mtx a,int b)
{
	mtx res=z;res.n=res.m=a.n;
	for(int i=1;i<=res.n;i++)res.t[i][i]=1;
	while(b)
	{
		if(b&1)res=res*a;
		a=a*a;
		b>>=1;
	}
	return res;
}

矩阵加速递推

给出 \(n(1\le n\le 10^{18})\),求斐波那契数列的第 \(n\) 项对 \(10000\) 取余。

根据递推式 \(F_i=F_{i-1}+F_{i-2}\),暴力做法是显然的,时间复杂度为 \(O(n)\)

定义初始矩阵 \(A=\begin{bmatrix}F_2 & F_1\end{bmatrix}\)\(B=\begin{bmatrix}1&1\\1&0\end{bmatrix}\)

\[A\times B=\begin{bmatrix}F_2 & F_1\end{bmatrix}\times\begin{bmatrix}1&1\\1&0\end{bmatrix}=\begin{bmatrix}F_2+F_1 & F_2\end{bmatrix}=\begin{bmatrix}F_3 & F_2\end{bmatrix} \]

那么要求 \(F_n\),就是求出 \(A\times B^{n-2}\) 的第一行第一列(当 \(n<2\) 时,直接输出 \(1\))。再用矩阵快速幂加速,时间复杂度为 \(O(\log n)\)

用类似的方法,还可以用矩阵乘法优化 dp

例题:GT 考试

全为数字的字符串,给你一个长度为 \(m\) 的字符串 \(T\),求出有多少个长度为 \(N\) 且不包含 \(T\) 的字符串(\(N\le 10^9,M\le 20\))。

\(f_{i,j}\)表示当前在第 \(i\) 位,匹配到了 \(T\) 串的第 \(j\) 位有多少可能的串,\(g_{k,j}\) 表示有多少种在当前匹配到 \(k\) 位的串加一个字符后匹配到第 \(j\) 位。那么就有

\[f_{i,j}=\sum_{k=1}^{M}f_{i-1,k}\times g_{k,j} \]

初始状态为 \(f_{0,0}=1\),答案为 \(\sum_{j=0}^{M-1}f_{n,j}\)
显然 \(g\) 是可以 \(O(M)\) KMP 求出的。状态转移可以看作只有一行的矩阵 \(f_j\) 表示当前匹配到第 \(j\) 位,向 \(i+1\) 时转移只需要用整个矩阵乘上 \(g\) 矩阵,显然可以用矩阵快速幂优化。

好题:Ex - add 1(矩阵加速期望 dp)


矩阵加速图上问题

例题:Walk

给定一张有 \(N\) 个节点的有向图,给出其邻接矩阵 \(A\),求图上所有长度为 \(K\) 的路径数(\(1\le N\le50, 1\le K\le 10^{18}\))。

\(f_{l,i,j}\) 表示从 \(i\)\(j\),长度为 \(l\) 的路径数,那么容易得到

\[f_{l,i,j}=\sum_{k=1}^{n}f_{l-1,i,k}\times A_{k,j} \]

可以发现将 \(f_{l-1}\) 乘上 \(A\),就得到了 \(f_{l}\) 矩阵。所以,\(A^K\) 中第 \(i\) 行第 \(j\) 列的元素表示从 \(i\)\(j\) 长度恰好为 \(K\) 的路径数。

例题:[SCOI2009] 迷路

有向图上边有边权 \(1\le v_i\le 9\),求从 \(1\)\(n\) 长度恰好为 \(K\) 的路径数,\(n\le 10\)

小 trick 之拆点。把一条长度为 \(l\) 的边拆成 \(l-1\) 个点,然后在从起点依次连到终点,然后这题就变成了 Walk。

例题:[USACO07NOV] Cow Relays

无向图上求从 \(S\)\(T\) 恰好经过 \(K\) 条边的最短路径。

小 trick 之矩阵乘法重定义。设 \(f_{l,i,j}\) 表示从 \(i\)\(j\) 恰好经过 \(l\) 条边的最短路长度,那么有

\[f_{l,i,j}=\min_{k=1}^{n}f_{l-1,i,k}+ A_{k,l} \]

其中 \(A\) 为原图的邻接矩阵,两点之间无边时,邻接矩阵中对应项为正无穷。
注意到以上状态转移的方式和矩阵乘法非常相似,如果将矩阵乘法重定义为以上运算,是不是就能用矩阵快速幂求出 \(A^K\) 呢?可以证明,以上矩阵运算具有结合律,所以也可以用类似快速幂的方式求出 \(A^K\)

例题:[NOI Online #3 提高组] 魔法值

每个节点在第 \(0\) 天有一个权值,往后每一天一个节点的权值会变成与其相邻所有节点的权值异或和,\(q\) 次询问,每次问第 \(t_i\) 天时 \(1\) 号点的权值(\(t_i\le 2^{32}\))。

\(f_{i,j}\) 表示 \(j\) 号节点第 \(i\) 天的权值,\(A\) 为原图的邻接矩阵,那么有

\[f_{i,j}=\oplus_{k=1}^{n}f_{i-1,k}A_{k,j} \]

其中 \(\oplus\) 表示按位异或运算。显然可以矩阵乘法重定义,然后每次询问快速幂 \(O(n^3\log t_i)\) 解决。但是有 \(q\le 200\) 次询问,总复杂度是不好的。
小 trick 之二进制拆分。考虑把询问按照 \(t\) 递增排序,每次只需要计算差值部分的幂,然后乘到上一次留下来的矩阵上。又注意到每次求幂的矩阵是相同的,所以考虑预处理出邻接矩阵的 \(2^i\) 次幂,计算差值部分时将对应次数乘到只有一行的权值矩阵上,复杂度可以降为 \(O(n^2)\)。这样的总复杂度是 \(O(n^3\log t+qn^2\log t)\),能过。

好题:[NOI2020] 美食家
把上面那一堆堆起来就可以过掉此题。


posted @ 2025-04-07 15:43  baiguifan  阅读(83)  评论(0)    收藏  举报