矩阵乘法学习笔记
矩阵乘法
对于线性简单的 dp
可以使用矩阵快速幂加速转移,可以从 O(n) 的时间复杂度降到 O(k3logn) ( k 为矩阵的大小,通常小于10)
先给出矩阵乘法的模板代码:
struct MAT {
int c[15][15],n,m;
MAT() {
memset(c,0,sizeof c);
}
MAT(int x,int y,int flag=0) {
n=x,m=y;
memset(c,0,sizeof c);
if(flag)for(int i=1; i<=n; i++)c[i][i]=1;
}
MAT operator *(const MAT &a)const {
MAT res(n,a.m);
for(int i=1; i<=n; i++)
for(int j=1; j<=a.m; j++)
res.c[i][j]=c[i][1]*a.c[1][j]+c[i][2]*a.c[2][j]+c[i][3]*a.c[3][j],res.c[i][j]%=mod;
return res;
}
} A[9],B(1,3);
MAT qpow(MAT A,int q) {
MAT res(A.n,A.m,1);
while(q) {
if(q&1)res=res*A;
q>>=1;
A=A*A;
}
return res;
}
这里乘的时候可以直接循环展开(手撕循环)常数大减。
以斐波那契数列讲一下矩阵乘法的实现。
fi=fi−1+fi−2
转移方程可以转化为 [fifi−1]∗[1110]=[fi+1fi]
这里的乘法就可以用快速幂来解决。实现 O(logn) 的时间复杂度。
普通的矩阵加速还是太简单了。是否有更难的矩阵的应用呢。
有的,有的。
这里有线段树维护矩阵的题 [大魔法师](Loading - 洛谷 | 计算机科学教育新生态 (luogu.com.cn))
但是其实就是码量大一点而已,其他就很简单,把每一个魔法对应的矩阵都算出来即可。
而矩阵在图上的应用就是类 Floyd 的算法,比如限长负环,限长最短路等,都是可以用矩阵乘法来加速的,本质来说就是倍增法。
分段 dp
:有些题可能dp
的每一部分的转移方程都是不同的,这样的话就是你把每一个矩阵都搞出来,离散化一下,对每一个区间求一遍即可。如[Runner's Problem](Runner's Problem - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)) (这都紫题了)
最重要的矩阵部分就是大名鼎鼎的动态dp
(DDP)(电灯泡)
何为动态 dp
, 就是本身就是一个 dp
,但是带修改的dp
,或者说是查询某一个区间,这时候用线段树维护矩阵就是一个不错的选择。
就像树上问题一样,第一步是转化成链上的问题。当然,动态dp
是先转化成普通的线性dp
。
比如,最大连续和这个经典的dp
,dp[i][0]=max(dp[i−1][0],dp[i−1][1]),dp[i][1]=dp[i−1][0]+a[i]
这个明显可以直接转化成矩阵的形式(当然是广义的矩阵乘法),[fi,0fi,1]∗[0a[i]0−inf]=[fi+1,0fi+1,1]
直接维护这些矩阵的就可以了,答案就是初始矩阵乘区间乘积即可。
当然难的 ddp
在树上的 ddp
但是蒟蒻还是太弱了,目前还是不会。