0%

矩阵快速幂运用

引入

想要了解矩阵快速幂,就不得不提到矩阵的概念。矩阵就像一个二维数组,存储了一组数据,如:

M=[123456789]M=\left[ \begin{matrix} 1&2&3\\ 4&5&6\\ 7&8&9\end{matrix} \right]

Mi,jM_{i,j} 表示矩阵第 ii 行第 jj 列的数据,如 M2,3=6M_{2,3}=6

矩阵运算

加法

矩阵的加法和实数加法类似,要求运算的两个矩阵大小相等,将对应位置相加即可。

减法

与加法相同,对应位置相减即可。

乘法

矩阵乘法并非对应位置相乘。

若矩阵 A×B=CA\times B=C 有意义,那么 AA 的列数要求与 BB 的行数相等。

AA 的列数与 BB 的行数等于 nn

Ci,j=k=1nAi,k×Bk,jC_{i,j}=\sum_{k=1}^{n}A_{i,k}\times B_{k,j}

即结果第 ii 行第 jj 列的值为 AA 的第 ii 行与 BB 的第 jj 列的对应项相乘后求和

一个很直观的理解是,把矩阵 AA 放在结果的左侧, 矩阵 BB 放在结果的下方,将行与列延长,对应相乘后结果写在交点处。

[123456789][36][123456789]\left[ \begin{matrix} \bold{1}&\bold{2}&\bold{3}\\ 4&5&6\\ 7&8&9\end{matrix} \right]\left[ \begin{matrix} \rightarrow&36&&\\ &\uparrow&&\\ &\uparrow&&\end{matrix} \right]\\ \,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\, \left[ \begin{matrix} 1&\bold{2}&3\\ 4&\bold{5}&6\\ 7&\bold{8}&9\end{matrix} \right]

注意矩阵乘法满足结合律,但是不一定满足交换律A×BA\times B 成立不一定代表 B×AB\times A 成立。

除法

\rightarrowTo be continued.

快速幂

快速幂可以 O(logn)O(\log n) 求解 ana^n,对矩阵同样适用。
代码:

1
2
3
4
5
6
int ans=1;
while(n){
if(n&1)ans*=a;
a*=a;
n>>=1;
}

矩阵快速幂

可以使用重载运算符的办法直接进行矩阵快速幂,方法和普通快速幂一样,但是要注意乘法顺序。ans的初始值要特别注意。如果想让它充当1,则需要设置成单位矩阵 II

[100001000010000001]\left[\begin{matrix}1&0&0&\cdots&0\\0&1&0&\cdots&0\\0&0&1&\cdots&0\\\vdots&\vdots&\vdots&\ddots&0\\0&0&0&0&1\end{matrix}\right]

递推

使用矩阵快速幂,可以用 O(logn)O(\log n) 的时间复杂度完成 nn 次递推关系,如斐波那契数列。矩阵可以通过递推关系推算出来。

[1101]×[Fn1Fn2]=[FnFn1]\because\left[\begin{matrix}1&1\\0&1\end{matrix}\right] \times \left[\begin{matrix}F_{n-1}\\F_{n-2}\end{matrix}\right] = \left[\begin{matrix}F_n\\F_{n-1}\end{matrix}\right]\\

Fn=[1101]n×[11]\therefore F_n=\left[\begin{matrix}1&1\\0&1\end{matrix}\right]^n \times \left[\begin{matrix}1\\1\end{matrix}\right]

推算矩阵

矩阵由转移关系得来。以斐波那契数列为例:

Fn=1Fn1+1Fn2\because F_n=1F_{n-1}+1F_{n-2}

Fn1=1Fn1+0Fn2F_{n-1}=1F_{n-1}+0F_{n-2}

M=[1101]\therefore M=\left[\begin{matrix}1&1\\0&1\end{matrix}\right]

其余类似。

例题

F(n,1)=F(n1,2)+2F(n3,1)+5F(n,1)=F(n-1,2)+2F(n-3,1)+5

F(n,2)=F(n1,1)+3F(n3,1)+2F(n3,2)+3F(n,2)=F(n-1,1)+3F(n-3,1)+2F(n-3,2)+3

已知

F(1,1)=2,F(1,2)=3,F(2,1)=1,F(2,2)=4,F(3,1)=6,F(3,2)=5F(1,1)=2,F(1,2)=3,F(2,1)=1,F(2,2)=4,F(3,1)=6,F(3,2)=5

F(n,1)F(n,1)F(n,2)F(n,2)

矩阵

[0100205100032310000000100000001000000010000000001]\left[ \begin{matrix} 0&1&0&0&2&0&5\\ 1&0&0&0&3&2&3\\ 1&0&0&0&0&0&0\\ 0&1&0&0&0&0&0\\ 0&0&1&0&0&0&0\\ 0&0&0&1&0&0&0\\ 0&0&0&0&0&0&1\\ \end{matrix} \right]

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include "bits/stdc++.h"
using namespace std;
const long long mod=99999999;
long long base[7][7]={
{0,1,0,0,2,0,5},
{1,0,0,0,3,2,3},
{1,0,0,0,0,0,0},
{0,1,0,0,0,0,0},
{0,0,1,0,0,0,0},
{0,0,0,1,0,0,0},
{0,0,0,0,0,0,1},
};
struct matrix{
long long v[30][30];
long long x,y;
};
matrix operator+(matrix a,matrix b){
matrix c;
if(a.x!=b.x||a.y!=b.y)return c;
long long x=a.x,y=a.y;
c.x=x;c.y=y;
for(long long i=0;i<x;i++){
for(long long j=0;j<y;j++){
c.v[i][j]=a.v[i][j]+b.v[i][j];
}
}
return c;
}
matrix operator-(matrix a,matrix b){
matrix c;
if(a.x!=b.x||a.y!=b.y)return c;
long long x=a.x,y=a.y;
c.x=x;c.y=y;
for(long long i=0;i<x;i++){
for(long long j=0;j<y;j++){
c.v[i][j]=a.v[i][j]-b.v[i][j];
}
}
return c;
}
matrix operator*(matrix a,matrix b){
matrix c;
if(a.y!=b.x)return c;
long long x=a.x,y=b.y,z=a.y;
c.x=x;c.y=y;
for(long long i=0;i<x;i++){
for(long long j=0;j<y;j++){
c.v[i][j]=0;
for(long long k=0;k<z;k++){
c.v[i][j]=(c.v[i][j]+a.v[i][k]*b.v[k][j]+mod)%mod;
}
}
}
return c;
}
void init(matrix &a){
for(long long i=0;i<a.x;i++){
for(long long j=0;j<a.y;j++){
a.v[i][j]=base[i][j];
}
}
}
ostream& operator<<(ostream& ous,matrix a){
for(long long i=0;i<a.x;i++){
for(long long j=0;j<a.y;j++){
ous<<a.v[i][j]<<' ';
}
ous<<endl;
}
return ous;
}
long long n;
matrix& operator *=(matrix &a,matrix n){
a=a*n;
return a;
}
matrix a,ans;
int main(){
cin>>n;
a.x=a.y=7;
init(a);
ans.x=7;ans.y=1;
ans.v[0][0]=6;
ans.v[1][0]=5;
ans.v[2][0]=1;
ans.v[3][0]=4;
ans.v[4][0]=2;
ans.v[5][0]=3;
ans.v[6][0]=1;
if(n==1){
cout<<2<<endl<<3;
}else if(n==2){
cout<<1<<endl<<4;
}else if(n==3){
cout<<6<<endl<<5;
}
if(n<4)return 0;
n-=3;
while(n){
if(n&1)ans=a*ans;
a*=a;
n>>=1;
}
cout<<ans.v[0][0]<<endl<<ans.v[1][0];
return 0;
}