矩阵快速幂
1.参考
2.定义
2.1 定义
如果直接求取 M^n,时间复杂度是 O(n),可以定义矩阵乘法,然后用快速幂算法来加速这里 M^n的求取, 简化时间复杂度为 O(logn)
主体思路就是不求 M^n 而是求 M^(n/2), 然后先不求M^(n/2), 先求M^(n/4)
具体实现同样可以用二进制分解加位掩码来实现(具体可以参考快速幂),只是当某个位掩码为 1 表示该位需要的时候,做的是快速幂做的是乘法,矩阵快速幂做的是矩阵乘法。
实现矩阵快速幂,可以先写一个 struct Matrix 表示矩阵,之后的乘法,求幂都是针对这个类的运算。
其中用一个二维数组做数据成员,一个 init () 方法,将矩阵初始化为单位阵 \(\begin{bmatrix}1&0\\0&1\end{bmatrix}\)。然后重载 * 和 ^ 分别表示矩阵乘法和矩阵快速幂。代码如下:
其中 M 表示方阵的行数。
2.2 代码模板
代码模板1
using ll = long long;
const int M = 2;
struct Ma
{
int a[M][M]; // 存取矩阵值
Ma()
{
memset(a, 0, sizeof(a));
}
void init() // 初始化单位矩阵
{
a[0][0] = a[1][1] = 1;
a[0][1] = a[1][1] = 0;
}
Ma operator*(const Ma& B) const // 矩阵乘法(结果固定为一个2*2的矩阵)
{
Ma ans;
for(int i = 0; i < M; ++i)
for(int j = 0; j < M; ++j)
for(int k = 0; k < M; ++k)
ans.a[i][j] += a[i][k] * B.a[k][j];
return ans;
}
Ma operator^(int n) const // 矩阵幂
{
Ma ans;
ans.init(); // 初始化一个单位矩阵
Ma A = *this; // 拷贝一个出来用于自乘
while(n)
{
if(n & 1)
ans = ans * A;
A = A * A;
n >>= 1;
}
return ans;
}
};
3.例题
3.1 LCR 126. 斐波那契数
链接LCR 126. 斐波那契数
这个模板不如上面那个通用,更多是针对本题的,可以学习参考
代码模板
vector<vector<long>> pow(vector<vector<long>>& a, int n) { // 矩阵幂
vector<vector<long>> ret{{1, 0}, {0, 1}};
while (n > 0) {
if (n & 1) { // 使用二进制分解的方法, 进行矩阵快速幂运算
ret = multiply(ret, a);
}
n >>= 1;
a = multiply(a, a); // 平方自增
}
return ret;
}
vector<vector<long>> multiply(vector<vector<long>>& a, vector<vector<long>>& b) { // 矩阵乘法
vector<vector<long>> c{{0, 0}, {0, 0}};
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
c[i][j] = (a[i][0] * b[0][j] + a[i][1] * b[1][j]) % MOD; // 这里因为提前知道是一个2*2矩阵 乘 一个2*2矩阵 = 一个2*2矩阵
}
}
return c;
}