矩阵加速
矩阵乘法
给你两个大小分别为 \((n,k),(k,m)\) 的矩阵,相乘得到 \(n,m\) 大小的矩阵 \(C_{i,j}=\sum\limits_{i=1}^{m} A_{i,k}*B_{k*j}\)
矩阵乘法没有交换律,有结合律和分配律
方阵
大小为 \(n,n\) 的矩阵。特点是可以自己乘自己。
其中主对角线为 \(1\) 的矩阵我们称作单位矩阵(\(E\)),那么有\(A*E=A\)
例如
\[\left[\begin{matrix}1&0&0\\0&1&0\\0&0&1\end{matrix}\right]
\]
矩阵快速幂
因为有矩阵快速幂,所以我们可以像整数一样进行快速幂
例题P3390
#include <bits/stdc++.h>
#define int long long
#define mod 1000000007
using namespace std;
const int MAXN = 105;
int n;
struct Matrix {
int m[MAXN][MAXN];
Matrix() {
memset(m, 0, sizeof(m));
}
}
;
Matrix multi(Matrix x, Matrix y) {
Matrix res;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
for (int t=1;t<=n;t++)
res.m[i][j]=(res.m[i][j]+x.m[i][t]*y.m[t][j]%mod)%mod;
return res;
}
Matrix quick_pow_matrix(Matrix a, int pw) {
Matrix ans;
for (int i=1;i<=n;i++)
ans.m[i][i]=1;
while(pw) {
if(pw&1)ans=multi(ans, a);
a=multi(a,a);
pw>>=1;
}
return ans;
}
signed main() {
int k;
Matrix a;
cin>>n>>k;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
cin>>a.m[i][j];
Matrix ans=quick_pow_matrix(a,k);
for (int i=1;i<=n;i++) {
for (int j=1;j<=n;j++)
printf("%lld ",ans.m[i][j]);
printf("\n");
}
return 0;
}
矩阵加速递推
斐波那契数列
定义 \(fib_1=fib_2=1,fib_n=fib_{n-1}+fib_{n-2}(n\geq 3)\)
我们考虑用矩阵快速幂
\[\left[\begin{matrix}
fib_{i-1}&fib_{i-2}
\end{matrix}\right]
\times
\left[\begin{matrix}
a&b\\c&d
\end{matrix}\right]
=
\left[\begin{matrix}
fib_i&fib_{i-1}
\end{matrix}\right]
\]
所以我们有
\[\begin{cases}fib_i=a*fib_{i-1}+c*fib_{i-2}\\fib_{i-1}=b*fib_{i-1}+d*fib_{i-2}
\end{cases}
\]
可以推出
\[\left[\begin{matrix}
fib_{i-1}&fib_{i-2}
\end{matrix}\right]
\times
\left[\begin{matrix}
1&1\\1&0
\end{matrix}\right]
=
\left[\begin{matrix}
fib_i&fib_{i-1}
\end{matrix}\right]
\]
代码实现也很简单,就是额外将 \(1\times 2\) 的矩阵扩充一排就可以了。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int SIZE = 10 + 10, MOD = 1000000007;
struct Matrix {
int val[SIZE][SIZE];
Matrix() {
memset(val, 0, sizeof val);
}
void BuildEpsilon(int n) {
for (int i = 1; i <= n; i ++) val[i][i] = 1;
}
}
;
Matrix Mul(Matrix x, Matrix y, int n) {
Matrix result;
for (int k = 1; k <= n; k ++) {
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n; j ++) {
result.val[i][j] += (x.val[i][k] * y.val[k][j]) % MOD;
result.val[i][j] %= MOD;
}
}
}
return result;
}
Matrix Qpow(Matrix base, int top, int n) {
Matrix result;
result.val[1][1] = result.val[1][2] = 1;
while(top) {
if(top & 1) result = Mul(result, base, n);
base = Mul(base, base, n);
top >>= 1;
}
return result;
}
signed main() {
#ifdef LOCAL
freopen("sample.in", "r", stdin);
freopen("sample.out", "w", stdout);
#endif
Matrix fibbase;
fibbase.val[1][1] = fibbase.val[1][2] = fibbase.val[2][1] = 1;
int k;
scanf("%lld", &k);
if(k <= 2) {
printf("1");
return 0;
}
fibbase = Qpow(fibbase, k - 2, 2);
printf("%lld", fibbase.val[1][1]);
return 0;
}
复杂的地推
我们提供一个简化题意:给你一个有 \(n\) 个空的环,每一个空可以填上绿色和金色两种,并且要求两个相邻的位置不能同时为绿色。
对于一条链的情况还是很好做的,但是我们如何处理头和尾的情况呢?
咕咕咕。

浙公网安备 33010602011771号