矩阵快速幂
矩阵快速幂是算法竞赛中一个非常实用的工具,它的核心价值在于:把 的线性递推优化到 ,从而解决
n极大(比如 量级)的问题。常见用途有以下几种:
- 快速求解线性递推数列
- 图论中的路径计数
- 状态压缩DP的加速
前置线性代数知识
矩阵乘法
NOTE注意矩阵快速幂中的矩阵一定是一个方阵,这样才有“幂”的概念。
下面的代码是通用的矩阵乘法形式,在矩阵快速幂中有
m == n。
for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) for (int k = 1; k <= n; k++) c[i][j] += a[i][k] * b[k][j];单位矩阵
主对角线全是 1,其他地方全是 0 的矩阵是单位矩阵,它乘任意矩阵的结果都等于任意矩阵本身。
矩阵快速幂
由于矩阵乘法满足结合律,一个单位矩阵用快速幂乘上 n 次就是矩阵快速幂了。
特殊地,定义 为单位矩阵 。
Code
// Problem: Luogu P3390// Contest: Luogu// URL: https://www.luogu.com.cn/problem/P3390// Time: 2026-05-08 12:56:14#include <bits/stdc++.h>using namespace std;
// clang-format off#define endl '\n'#define all(x) (x).begin(), (x).end()#define fastio() ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);// clang-format on
using ll = long long;using ull = unsigned long long;using pii = pair<int, int>;using pdd = pair<double, double>;using pll = pair<long long, long long>;
const int dx[] = {-1, 0, 1, 0, -1, 1, 1, -1};const int dy[] = {0, 1, 0, -1, 1, 1, -1, -1};const int inf = 0x3f3f3f3f;const int N = 101;const ll mod = 1e9 + 7;
auto matrix_mul(const vector<vector<ll>> &a, const vector<vector<ll>> &b){ int n = (int) a.size() - 1; vector<vector<ll>> res(n + 1, vector<ll>(n + 1, 0));
for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) for (int k = 1; k <= n; k++) res[i][j] = (res[i][j] + a[i][k] * b[k][j]) % mod;
return res;}
auto matrix_power(vector<vector<ll>> m, ll k){ int n = m.size() - 1;
vector<vector<ll>> res(n + 1, vector<ll>(n + 1, 0)); // 注意一开始res是单位矩阵
for (int i = 1; i <= n; i++) res[i][i] = 1;
while (k) // 和快速幂相同 { if (k & 1) res = matrix_mul(res, m);
m = matrix_mul(m, m);
k >>= 1; }
return res;}
void solve(){ int n; ll k; cin >> n >> k;
vector<vector<ll>> a(n + 1, vector<ll>(n + 1));
for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) cin >> a[i][j];
auto res = matrix_power(a, k);
for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) cout << res[i][j] << " \n"[j == n];}
int main(){ fastio();
int T = 1; // cin >> T;
while (T--) solve();
return 0;}矩阵加速
了解了矩阵快速幂的基本算法以后,我们可以在很多情况下利用矩阵快速幂实现矩阵加速。
矩阵加速只适用于固定关系的递推式,也就是说不适用于存在条件转移的情况。
固定关系的一维一阶递推表达式,可以用基本的乘法快速幂解决,时间复杂度 ,可以推广到一维 阶。
例如有一个递推表达式为 ,我们在求每一项时只要知道前一项的值就可以了,这样的表达式称作一维一阶的递推式。
固定关系的一维 阶递推表达式,可以用矩阵快速幂解决,时间复杂度 。
例如,
Fibonacci数列的递推表达式是 ,这就是一个一维二阶的递推式。一维二阶的递推式一定存在一个2x2的关系矩阵。
使用矩阵加速的难点在于构造初始的关系矩阵(也就是“单位矩阵”),关系矩阵的第1列直接由递推表达式的系数决定,剩下的项可以通过前面的初始项代入求解。
矩阵加速模板
将矩阵乘法和矩阵快速幂封装在一个结构体里,方便调用模板。
使用重载运算符的方式进行矩阵乘法,下面进行一些代码实现的说明:
- 当写下
A * B时,实际上是A.operator*(B),左参数会通过一个隐藏的指针this进行隐式调用,可以使用this -> mat来访问。右参数是显式传入的。 - 矩阵乘法中循环顺序为
i, k, j可以加快计算速度。 assert()用于断言表达式,如果表达式错误,程序会输出错误信息并终止,否则正常运行。详见:assert - cppreference.cn - C++参考手册
struct Matrix{ int r, c; vector<vector<ll>> mat;
// 构造函数:初始化为 r 行 c 列的零矩阵 Matrix(int r, int c) : r(r), c(c), mat(r, vector<ll>(c, 0)){}
// 重载乘法运算符 Matrix operator*(const Matrix &other) const { // 结果矩阵的行数等于左矩阵的行数,列数等于右矩阵的列数 Matrix res(r, other.c);
for (int i = 0; i < r; i++) { for (int k = 0; k < c; k++) { // 如果乘数为 0,直接跳过内层循环 if (mat[i][k] == 0) continue; for (int j = 0; j < other.c; j++) res.mat[i][j] = (res.mat[i][j] + mat[i][k] * other.mat[k][j]) % mod; } } return res; }
Matrix power(ll k) const // 矩阵快速幂 { assert(r == c); Matrix res(r, c);
for (int i = 0; i < r; i++) // 初始化单位矩阵 res.mat[i][i] = 1;
Matrix base = *this; while (k > 0) { if (k & 1) res = res * base; base = base * base; k >>= 1; } return res; }};Fibonacci
例题链接:P1962 斐波那契数列 - 洛谷
我们首先假设关系矩阵是 ,由于关系式 ,等号右侧的两个数系数都是 1,直接确定 a = 1, b = 1。由于初始矩阵是 ,也就是 Fibonacci 数列的第 1 项和第 0 项,我们已知它乘上关系矩阵以后,一定可以得到 Fibonacci 数列的第 2 项和第 1 项,因此直接模拟一次矩阵乘法求出 c 和 d。
最终的矩阵递推式:,注意特判边界情况:求前两项直接输出 1 即可。
Code
// Problem: Luogu P1962// Contest: Luogu// URL: https://www.luogu.com.cn/problem/P1962// Time: 2026-05-08 13:36:52#include <bits/stdc++.h>using namespace std;
// clang-format off#define endl '\n'#define all(x) (x).begin(), (x).end()#define fastio() ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);// clang-format on
using ll = long long;using ull = unsigned long long;using pii = pair<int, int>;using pdd = pair<double, double>;using pll = pair<long long, long long>;
const int dx[] = {-1, 0, 1, 0, -1, 1, 1, -1};const int dy[] = {0, 1, 0, -1, 1, 1, -1, -1};const int inf = 0x3f3f3f3f;const int N = 0;const ll mod = 1e9 + 7;
struct Matrix{ int r, c; vector<vector<ll>> mat;
Matrix(int r, int c) : r(r), c(c), mat(r, vector<ll>(c, 0)) { }
Matrix operator*(const Matrix &other) const { Matrix res(r, other.c);
for (int i = 0; i < r; i++) { for (int k = 0; k < c; k++) { if (mat[i][k] == 0) continue; for (int j = 0; j < other.c; j++) { res.mat[i][j] = (res.mat[i][j] + mat[i][k] * other.mat[k][j]) % mod; } } } return res; }
Matrix power(ll k) const { assert(r == c); Matrix res(r, c);
for (int i = 0; i < r; i++) res.mat[i][i] = 1;
Matrix base = *this; while (k > 0) { if (k & 1) res = res * base; base = base * base; k >>= 1; } return res; }};
void solve(){ ll n; cin >> n;
if (n <= 2) { cout << 1 << endl; return; }
// 构造转移矩阵(关系矩阵) Matrix base(2, 2); base.mat[0][0] = 1; base.mat[0][1] = 1; base.mat[1][0] = 1; base.mat[1][1] = 0;
Matrix res = base.power(n - 1);
cout << res.mat[0][0] << endl;}
int main(){ fastio();
int T = 1; // cin >> T;
while (T--) solve();
return 0;}矩阵加速数列2
构造初始矩阵更通用的方法就是,当明确目标矩阵以后,像这样推一下递推式。

Code
// Problem: Luogu P1939// Contest: Luogu// URL: https://www.luogu.com.cn/problem/P1939// Time: 2026-05-12 15:59:21#include <bits/stdc++.h>using namespace std;
// clang-format off#define endl '\n'#define all(x) (x).begin(), (x).end()#define fastio() ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);// clang-format on
using ll = long long;using ull = unsigned long long;using pii = pair<int, int>;using pdd = pair<double, double>;using pll = pair<long long, long long>;using i128 = __int128;
const int dx[] = {-1, 0, 1, 0, -1, 1, 1, -1};const int dy[] = {0, 1, 0, -1, 1, 1, -1, -1};const int inf = 0x3f3f3f3f;const int N = 0;const ll mod = 1e9 + 7;
struct Matrix{ int r, c; vector<vector<ll>> mat;
Matrix(int r, int c) : r(r), c(c), mat(r, vector<ll>(c, 0)) { }
Matrix operator*(const Matrix &other) const { Matrix res(r, other.c);
for (int i = 0; i < r; i++) { for (int k = 0; k < c; k++) { if (mat[i][k] == 0) continue; for (int j = 0; j < other.c; j++) { res.mat[i][j] = (res.mat[i][j] + mat[i][k] * other.mat[k][j]) % mod; } } } return res; }
Matrix power(ll k) const { assert(r == c); Matrix res(r, c);
for (int i = 0; i < r; i++) res.mat[i][i] = 1;
Matrix base = *this; while (k > 0) { if (k & 1) res = res * base; base = base * base; k >>= 1; } return res; }};
int main(){ fastio();
int T = 1; cin >> T;
Matrix base(3, 3); base.mat[0][0] = 1, base.mat[0][1] = 0, base.mat[0][2] = 1; base.mat[1][0] = 1, base.mat[1][1] = 0, base.mat[1][2] = 0; base.mat[2][0] = 0, base.mat[2][1] = 1, base.mat[2][2] = 0;
while (T--) { ll n; cin >> n;
if (n <= 3) { cout << 1 << endl; continue; }
Matrix res = base.power(n); cout << res.mat[1][0] << endl; }
return 0;}