矩阵加速

矩阵乘法

给你两个大小分别为 \((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;
}

复杂的地推

P4910 帕秋莉的手环

我们提供一个简化题意:给你一个有 \(n\) 个空的,每一个空可以填上绿色和金色两种,并且要求两个相邻的位置不能同时为绿色。

对于一条链的情况还是很好做的,但是我们如何处理头和尾的情况呢?

P4838 P哥破解密码

咕咕咕。

posted @ 2021-11-15 08:05  Gloamin  阅读(66)  评论(0)    收藏  举报