矩阵乘法与优化
矩阵乘法
0 | 1 |
---|---|
1 | 1 |
这是一个矩阵,那么我要让它乘以一个这样的矩阵
1 | 0 |
---|---|
0 | 1 |
那么它的结果就是
0 | 1 |
---|---|
1 | 1 |
如果乘以它自身,那么它的结果就是
1 | 1 |
---|---|
1 | 2 |
那么矩阵乘法的公式就应该是
(此图为网图,侵权可以私信我)
可以发现,矩阵乘法的右单位元应该是
1 | 0 | 0 |
---|---|---|
0 | 1 | 0 |
0 | 0 | 1 |
后面的以此类推
因为对于当前行的每一列都会都会乘以一个对应的数,那么当前列要保留的数所对应的位置就应该是 猜测推算就可以得出上述矩阵。
另外矩阵乘法满足结合律,证明我也不会 (T﹏T)。
矩阵乘法优化递推
斐波那契数列
斐波那契数列你显然可以用
但是如果
由于
0 | 1 |
---|---|
1 | 1 |
由于下面还要用,所以将该矩阵设为
所以我们就可以知道
由于满足结合律可以拆括号为
还能简化为
那么就很好算了呀,代码如下
#include <algorithm> #include <iostream> using namespace std; using ll = long long; const int MaxN = 110, mod = 1e9 + 7; struct A { int n, m; ll a[MaxN][MaxN]; A() { fill(a[1], a[MaxN], 0); } void build() { for (int i = 1; i < MaxN; i++) { a[i][i] = 1; } } void init() { for (int i = 1; i < m; i++) { a[i + 1][i] = 1; } for (int i = 1; i <= n; i++) { a[i][m] = 1; } } A operator*(const A &b) const { A c; c.n = n, c.m = b.m; for (int i = 1; i <= n; i++) { for (int k = 1; k <= m; k++) { for (int j = 1; j <= b.m; j++) { c.a[i][j] = (c.a[i][j] + a[i][k] * b.a[k][j] % mod) % mod; } } } return c; } } a, c; ll n, m = 2; A qpow(A a, ll b) { A res; res.build(), res.n = a.n, res.m = a.m; for (ll i = 1; i <= b; i <<= 1) { if (b & i) { res = res * a; } a = a * a; } return res; } int main() { ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); cin >> n; a.n = 1, a.m = m; a.a[a.n][a.m] = 1; c.n = c.m = m, c.init(); c = qpow(c, n), a = a * c; cout << a.a[1][m] << '\n'; return 0; }
那么如果是
那么我们其实可以理解为斐波那契数列的扩展,那么同样的我们可以得到变化是要乘的矩阵应该是,对于每个
代码如下
#include <algorithm> #include <iostream> using namespace std; using ll = long long; const int MaxN = 110, mod = 1e9 + 7; struct A { int n, m; ll a[MaxN][MaxN]; A() { fill(a[1], a[MaxN], 0); } void build() { for (int i = 1; i < MaxN; i++) { a[i][i] = 1; } } void init() { for (int i = 1; i < m; i++) { a[i + 1][i] = 1; } for (int i = 1; i <= n; i++) { a[i][m] = 1; } } A operator*(const A &b) const { A c; c.n = n, c.m = b.m; for (int i = 1; i <= n; i++) { for (int k = 1; k <= m; k++) { for (int j = 1; j <= b.m; j++) { c.a[i][j] = (c.a[i][j] + a[i][k] * b.a[k][j] % mod) % mod; } } } return c; } } a, c; ll n, m; A qpow(A a, ll b) { A res; res.build(), res.n = a.n, res.m = a.m; for (ll i = 1; i <= b; i <<= 1) { if (b & i) { res = res * a; } a = a * a; } return res; } int main() { ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); cin >> n >> m; a.n = 1, a.m = m; a.a[a.n][a.m] = 1; c.n = c.m = m, c.init(); c = qpow(c, n), a = a * c; cout << a.a[1][m] << '\n'; return 0; }
P2106 Sam数
思路
首先考虑暴力求解,我们设
然后我们会发现这样子不管是时间还是空间,都是会爆的,因为这题线性都不行,那么我们就需要矩阵乘法优化,我们发现一点,
code
#include <algorithm> #include <iostream> using namespace std; using ll = long long; const int MaxN = 20, mod = 1000000007; struct A { int n, m; ll a[MaxN][MaxN]; A() { fill(a[0], a[MaxN], 0); } void build() { for (int i = 0; i < MaxN; i++) { a[i][i] = 1; } } void init() { for (int i = 0; i <= n; i++) { for (int j = max(i - 2, 0); j <= min(i + 2, m); j++) { a[i][j] = 1; } } } A operator*(const A &b) const { A c; c.n = n, c.m = b.m; for (int i = 0; i <= n; i++) { for (int k = 0; k <= m; k++) { for (int j = 0; j <= b.m; j++) { c.a[i][j] = (c.a[i][j] + a[i][k] * b.a[k][j] % mod) % mod; } } } return c; } } a, c; ll n, m = 9, ans; A qpow(A a, ll b) { A res; res.build(), res.n = a.n, res.m = a.m; for (ll i = 1; i <= b; i <<= 1) { if (b & i) { res = res * a; } a = a * a; } return res; } int main() { ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); cin >> n; a.n = 0, a.m = m; for (int i = 0; i <= m; i++) { a.a[0][i] = 1; } c.n = c.m = m, c.init(); c = qpow(c, n - 1), a = a * c; for (int i = 1; i <= m; i++) { ans = (ans + a.a[0][i]) % mod; } cout << ans + (n == 1) << endl; // 如果位数是1的话那么前导0不是前导了 return 0; }
本文作者:yabnto
本文链接:https://www.cnblogs.com/ybtarr/p/17472380.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步