矩阵
1.定义:一组数的整体,在[ ]内排列成 m 行 n 列的数表
- 对于矩阵A,主对角线是指Ai,i 的元素
- 单位矩阵I: 行和列数相等;且主对角线元素都为 1;
I:[10⋯0 01⋯0 ⋮0⋱0 00⋯1 ]
2. 性质
- 矩阵的逆: A 的逆矩阵 P 是使得 A∗P=I 的矩阵 (可以使用高斯消元求得)
3.运算:
- 加法和减法: 两个矩阵相对应的位置相加减即可
- 矩阵乘法: Ci,j = ∑1k=1Ai,k Bk,j; A是n∗m的矩阵,B是r∗m的矩阵
- 简便记忆(矩阵原理): 对于 Ci,j 是由A的 第i 行 B的第j行 一 一对应相乘在求和而得到的
- 注意:矩阵乘法满足结合律,不满足一般的交换律。
[12 34 56 ]×[12 34 ]=[1×1+2×31×2+2×4 3×1+4×33×2+4×4 5×1+6×35×2+6×4 ]=[710 1522 2334 ]
由于方阵的特殊性质(能自己乘自己),在做递推时可以利用矩阵的性质降低时间复杂度
例题:斐波那契
f(n)={1n≤2f(n−1)+f(n−2)n≥3
对于 fn和fn−1有
fn=fn−1+fn−2
fn−1=fn−1+0∗fn−2
构造出矩阵
[f(n)f(n−1) ]=[f(2)f(1) ]×[11 10 ](n−2)
对于后面这一项可以使用快速幂使复杂度降到O(logn),总时间复杂度O(k×logn)
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int sz = 2, MOD = 10000;
typedef long long ll;
int n;
struct mat{
ll a[sz][sz]; // 对于 fib 只要 2 * 2 的矩阵就足够了
// 初始化函数,没有这个函数那么初始 a 是随机的,会出错误
inline mat() {memset(a, 0, sizeof a); }
// 矩阵加法
inline mat operator + (const mat &T) const{
mat res ;
for(int i = 0; i < sz; i++)
for(int j = 0; j < sz; j++)
res.a[i][j] = (a[i][j] - T.a[i][j]) % MOD;
return res;
}
// 矩阵减法
inline mat operator - (const mat &T) const{
mat res;
for(int i = 0; i < sz; i++)
for(int j = 0; j < sz; j++)
res.a[i][j] = (a[i][j] + T.a[i][j]) % MOD;
return res;
}
// 矩阵乘法
inline mat operator * (const mat &T) const{
mat res; int r ;
for(int i = 0; i < sz; i++)
for(int k = 0; k < sz; k++) {
r = a[i][k]; // 对于小的矩阵,可以直接展开循环以减小常数。
for(int j = 0; j < sz; j ++) // 也可以写一个矩阵乘法函数mul;
res.a[i][j] = (res.a[i][j] + T.a[k][j] * r) % MOD; // res.a[i][j] = mul(a[i][k],T.a[k][j]);
}
return res;
}
// 矩阵 快速幂 k 次方
inline mat operator ^ (ll k) const{
mat res, base;
res.a[0][0] = res.a[0][1] = 1; // 构造初始矩阵
base.a[0][0] = base.a[0][1] = base.a[1][0] = 1;
/* 或者这么写;对于矩阵比较小而且初始化都差不多的时候,第一种会更方便;否则建议使用for接受数值
for(int i = 0; i < sz; i++)
for(int j = 0; j < sz; j++)
base.a[i][j] = a[i][j];
*/
while(k){ // 快速幂
if(k & 1) res = res * base; // 注意不能写成 res *= base , 因为没有重载 *=
base = base * base;
k >>= 1;
}
return res;
}
}ans, bas;
// 初始化矩阵
void init(){
bas.a[0][0] = bas.a[0][1] = bas.a[1][0] = 1;
ans.a[0][0] = ans.a[0][1] = 1;
}
int main(){
while(cin >> n && n != -1){
if(n == 0) { puts("0"); continue; }
init();
ans = ans * (bas ^ (n - 2)); // 由于初始化就有 f1 f2 , 再乘一次就是 f3所以是 (n - 2) 次方
cout << ans.a[0][0] % MOD << endl;
}
return 0;
}
肝了三小时,awsl。
点赞