矩阵
定义
在数学中,矩阵(Matrix)是一个按照长方阵列排列的复数或实数集合。
就像下面一样:
特别的,主对角线上为 1,其余位置为 0 的叫做单位矩阵 \(I\):
矩阵的运算
矩阵的加减法是逐个元素进行的。
乘法
矩阵相乘只有在第一个矩阵的列数和第二个矩阵的行数相同时才有意义。
设 \(P\) 为 \(M \times N\) 的矩阵,\(Q\) 是 \(N \times Q\) 的矩阵,\(C\) 矩阵为 \(C = PQ\),则 \(C\) 矩阵上 \(i\) 行 \(j\) 列的元素为。
没有看懂,没事,有一种比较简单的方法。
设 \(A\) 矩阵为:
设 \(B\) 矩阵为:
所以 \(A\) 与 \(B\) 的乘积是:
注意,矩阵乘法满足结合律,不满足一般的交换律。
给出一个封装好的矩阵乘法:
struct mat{
long long c[N][N];
void unit(){
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++) c[i][i] = 1;
}
void mat(){memset(c,0,sizeof(c));}
inline mat operator*(const mat &M){
mat ans;
memset(ans.c,0,sizeof(ans.c);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
ans.c[i][j] += (c[i][k] * M.c[k][j]) % P,ans.c[i][j] %= P;
return ans;
}
};
如果算一个矩阵的 \(A^k\),可以使用快速幂来加速到 \(\mathcal{O}(n^3 \log k)\)。
// 快速幂
mat qpow(mat x,int y){
mat ans.unit();
while(y > 0){
if(y % 2 == 1)
ans = ans * x;
y = y / 2;
x = x * x;
}
return ans;
}
矩阵的应用
快速求斐波拉契数列
\(F_1 = F_2 = 1\),\(F_i = F_{i-1} + F_{i-2} \ (i \geq 3)\) 就是斐波拉契数列。
当然可以使用递推的方法来求,时间复杂度 \(\mathcal{O}(n)\) ,还挺快。
但是遇到这道题目就不行了,\(2^{63}\) 绝对会 TLE,所以可以用矩阵加速递推。
我们可以设 \(f(n) = \begin{bmatrix}F_{n} & F_{n-1}\end{bmatrix}\) ,我们希望可以从 \(f(n-1)\) 推出它。
正好我们之前学了矩阵的乘法,所以我们可以尝试设一个矩阵 \(\text{base}\) ,使得 \(f(n) = \text{base} \times f(n-1)\),也就是:
因为前后都是 \(1 \times 2\) 的矩阵,所以可以知道 \(\text{base}\) 矩阵是 \(2 \times 2\) 的矩阵。
所以 \(\text{base} = \begin{bmatrix} a & b \\ c & d \\ \end{bmatrix}\)
所以等式转化为:
又因为 \(F_i = F_{i-1} + F_{i-2}\),所以 \(a = 1,c = 1,b = 1,d = 0\)。
综上所述, \(\text{base} = \begin{bmatrix} 1 & 1\\ 1 & 0\end{bmatrix}\)。
那么因为初始项为 \(F_1,F_2\)。所以 \(F_n = \begin{bmatrix} F_1 & F_2 \end{bmatrix} \times \text{base}^{n-2}\)
好好品品。
\(\text{base}^{n-2}\) 矩阵可以通过上面的快速幂来求,所以时间复杂度为 \(\mathcal{O}(\log n)\)。
这题核心代码:
struct mat{
long long c[4][4];
mat() {memset(c,0,sizeof(c));}
inline mat operator*(const mat& M){
mat res;
for(int i=1;i<=2;i++)
for(int j=1;j<=2;j++)
for(int k=1;k<=2;k++)
res.c[i][j] = (res.c[i][j] + c[i][k]*M.c[k][j]) % mod;
return res;
}
};
mat base,ans;
void init(){
base.c[1][1] = base.c[1][2] = base.c[2][1] = 1;
ans.c[1][1] = ans.c[1][2] = 1;
}
void qpow(long long y){
while(y > 0){
if(y % 2 == 1)
ans = ans * base;
y = y / 2;
base = base * base;
}
}
其实矩阵可以干事情的很多,就写这么多吧