矩阵加速学习笔记

前置知识:矩阵乘法,快速幂

引入

当我们在做题时,常常发现对于某个问题而言可以轻而易举地推出求解的递推式,但如何快速求解就像时挡在我们面前的一座大山,现在我门就来系统的说说如何通过矩阵来加速此类运算。

例子:斐波那契数列

递推式:

fi={1,i2 fi1+fi2,otherwise

当需要求解的范围在107内时我们可以使用O(n)的方法来进行求解,但是如果n107 时就显得心有余而力不足了,这时候矩阵快速幂就可以解决此类问题。

我们定义一个 1×2 的矩阵

mi=[fifi1]

则:

mi1=[fi1fi2]

不难发现,

mi=mi1×[11 10]

那么:

mi=m2×[11 10]i2

Talk is cheap. Show me the code.

洛谷 P1962 斐波那契数列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <cstdint>
#include <cstring>
#include <iostream>

using namespace std;

typedef int_fast64_t int64;

constexpr int64 MOD = 1e9 + 7;

int64 n;

class Matrix {
public:
int64 row, col;
int64 num[2][2];
Matrix(const int64 row, const int64 col) {
this->row = row;
this->col = col;
memset(num, 0, sizeof(num));
}
};

inline Matrix operator*(const Matrix &lMat, const Matrix &rMat) {
Matrix res(lMat.row, rMat.col);
for (int64 i = 0; i < res.row; ++i) {
for (int64 k = 0; k < lMat.col; ++k) {
for (int64 j = 0; j < res.col; ++j) {
res.num[i][j] =
(res.num[i][j] + (lMat.num[i][k] * rMat.num[k][j]) % MOD) % MOD;
}
}
}
return res;
}

Matrix base(2, 2), ans(1, 2);

void qpow(int64 y) {
while (y) {
if (y & 1) {
ans = ans * base;
}
base = base * base;
y >>= 1;
}
}

int main() {
ios::sync_with_stdio(false);

cin >> n;

if (n <= 2) {
cout << 1 << endl;
return 0;
}

base.num[0][0] = 1;
base.num[0][1] = 1;
base.num[1][0] = 1;

ans.num[0][0] = 1;
ans.num[0][1] = 1;

qpow(n - 2);
cout << ans.num[0][0] % MOD << endl;

return 0;
}

如何构造常系数矩阵

在我们推导出递推式后,如何构造一个矩阵来进行矩阵快速幂就成为难点,以下总结了一些常用的方法来构造这个常数矩阵。

无常数项

例子:

fi=fi1+fi3

由于 fifi1fi3 有关,并没有其中的 fi2,如果我们将矩阵设置为:

mi=[fifi3]

那么就无法通过左乘另一个矩阵来获取 mi+1

我们换一种角度思考,当我们需要获取 fi 时,需要知道 fi1fi3,那么我们就必须要在 mi1 这个矩阵中包含 fi1fi3,则一种新的设计为:

mi=[fifi2]

那么 fi2 需要知道 fi5,这又令我们难受了,于是我们再次重新设计:

mi=[fifi1fi2]

那么这一次惊讶滴发现

mi1=[fi1fi2fi3]

似乎可以构造常系数矩阵了?

设常系数矩阵为:

K=[k0,0k0,1k0,2 k1,0k1,1k1,2 k2,0k2,1k2,2]

那么就可以使用如下方法计算 mi (由于无法使用双下标,所以这里的 M(i,j) 表示的是 M 矩阵的元素)

mi=[fi1+fi3=j=02mi1(0,j)K(j,0)fi2=j=02mi1(1,j)K(j,1)fi3=j=02mi1(2,j)K(j,2)]

可以得出

K=[110 001 100]

常数项与 n 无关

fi=fi1+fi2+1

易得:

mi1=[fi1fi2fi31]

K=[1100 1010 0000 1001 ]

常数项与 n1 有关

fi=fi1+fi2+i2

由于矩阵 mi1 需要包含 fi1fi2i2,可知 mi 需要包含 fifi1i1

$$
m_{i} =
[fifi1i1] \

m_{i - 1} =
[fi1fi2i2]
$$

转移的时候需要将 i2 变换为 i1,只需要在矩阵中在添加一个元素 1 即可,最终的矩阵如下:

mi=[fifi1i11]

常系数矩阵如下:

K=[1100 1000 1010 0011]