矩阵优化
前几天武理的新生赛考了道矩阵优化的斐波那契,感觉忘得差不多了
前置知识
- 矩阵乘法
一个 n * p 的矩阵 A 和一个 p * m 的矩阵 B 相乘得到 n * m 的矩阵 C ,其中 C[i][j] = ∑A[i][k] * B[k][j] ; (k = 1, 2, 3... n)
- 矩阵快速幂
与普通快速幂相似,初始化cnt, x,再重载一下运算符,即可直接套ksm的板子
node ksm(ll y) { node cnt, x; 初始化cnt, x; while(y) { if(y & 1) cnt = cnt * x; x = x * x; y >>= 1; } return cnt; }
矩阵优化
- 适用范围
- n 非常大,通常在long long
- 转移决策较少,系数为常数
- 通常会对一个数取模
矩阵优化能在 O(logn) 的时间内算出 f[n] 的值
- 步骤
以斐波那契数列为例
1. 计算递推式
f[n] = f[n - 1] + f[n - 2];
2. 构造矩阵
我们将 {f[i], f[i - 1]} 视为一个 1 * 2 的矩阵,需要乘上一个 2 * 2 的矩阵使其转化成 {f[i], f[i - 1]},即向后递推一位
通过计算可得,这个 2 * 2 的矩阵 x 为
3. 矩阵快速幂
通过矩阵进行递推,将原本只需一步的转移变成多次加乘,看起来更加复杂了。但是,矩阵的转移可以用上快速幂,n 次转移即相当于 {f[i], f[i - 1]} * x ^ n
- 模板
#include <bits/stdc++.h> using namespace std; #define ll long long const ll mod = 1000000007; struct node { ll dp[3][3]; void clear() {memset(dp, 0, sizeof(dp));} }f, ans; node operator * (node x, node y) { node cnt; cnt.clear(); for(int i = 1; i <= 2; i++) for(int j = 1; j <= 2; j++) for(int k = 1; k <= 2; k++) cnt.dp[i][j] = (cnt.dp[i][j] + x.dp[i][k] * y.dp[k][j] % mod) % mod; return cnt; } node ksm(ll y) { node cnt, x; cnt.clear(); x.clear(); cnt.dp[1][1] = cnt.dp[1][2] = 1; x.dp[1][1] = x.dp[1][2] = x.dp[2][1] = 1; while(y) { if(y & 1) cnt = cnt * x; x = x * x; y >>= 1; } return cnt; } int main() { ll n; cin >> n; if(n <= 2) {cout << 1; return 0;} f.dp[1][1] = f.dp[1][2] = 1; node ans = ksm(n - 2);//{f[2], f[1]}经过n - 2次转移得到{f[n], f[n - 1]} cout << ans.dp[1][1]; return 0; }