矩阵乘法学习笔记
矩阵乘法
设 \(A\) 为一个 \(n\times m\) 的矩阵, \(B\) 为一个 \(m\times p\) 的矩阵,\(C\) 为矩阵 \(A\) 与 \(B\) 的乘积,那么有
\(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}\)。
有
那么要求 \(F_n\),就是求出 \(A\times B^{n-2}\) 的第一行第一列(当 \(n<2\) 时,直接输出 \(1\))。再用矩阵快速幂加速,时间复杂度为 \(O(\log n)\)。
用类似的方法,还可以用矩阵乘法优化 dp。
全为数字的字符串,给你一个长度为 \(m\) 的字符串 \(T\),求出有多少个长度为 \(N\) 且不包含 \(T\) 的字符串(\(N\le 10^9,M\le 20\))。
设 \(f_{i,j}\)表示当前在第 \(i\) 位,匹配到了 \(T\) 串的第 \(j\) 位有多少可能的串,\(g_{k,j}\) 表示有多少种在当前匹配到 \(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)
矩阵加速图上问题
给定一张有 \(N\) 个节点的有向图,给出其邻接矩阵 \(A\),求图上所有长度为 \(K\) 的路径数(\(1\le N\le50, 1\le K\le 10^{18}\))。
设 \(f_{l,i,j}\) 表示从 \(i\) 到 \(j\),长度为 \(l\) 的路径数,那么容易得到
可以发现将 \(f_{l-1}\) 乘上 \(A\),就得到了 \(f_{l}\) 矩阵。所以,\(A^K\) 中第 \(i\) 行第 \(j\) 列的元素表示从 \(i\) 到 \(j\) 长度恰好为 \(K\) 的路径数。
有向图上边有边权 \(1\le v_i\le 9\),求从 \(1\) 到 \(n\) 长度恰好为 \(K\) 的路径数,\(n\le 10\)。
小 trick 之拆点。把一条长度为 \(l\) 的边拆成 \(l-1\) 个点,然后在从起点依次连到终点,然后这题就变成了 Walk。
无向图上求从 \(S\) 到 \(T\) 恰好经过 \(K\) 条边的最短路径。
小 trick 之矩阵乘法重定义。设 \(f_{l,i,j}\) 表示从 \(i\) 到 \(j\) 恰好经过 \(l\) 条边的最短路长度,那么有
其中 \(A\) 为原图的邻接矩阵,两点之间无边时,邻接矩阵中对应项为正无穷。
注意到以上状态转移的方式和矩阵乘法非常相似,如果将矩阵乘法重定义为以上运算,是不是就能用矩阵快速幂求出 \(A^K\) 呢?可以证明,以上矩阵运算具有结合律,所以也可以用类似快速幂的方式求出 \(A^K\)。
每个节点在第 \(0\) 天有一个权值,往后每一天一个节点的权值会变成与其相邻所有节点的权值异或和,\(q\) 次询问,每次问第 \(t_i\) 天时 \(1\) 号点的权值(\(t_i\le 2^{32}\))。
设 \(f_{i,j}\) 表示 \(j\) 号节点第 \(i\) 天的权值,\(A\) 为原图的邻接矩阵,那么有
其中 \(\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] 美食家
把上面那一堆堆起来就可以过掉此题。
浙公网安备 33010602011771号