矩阵乘法基本运算
如图,矩阵A*B就是用A的每一行依次乘B的每一列
就是A的第i行中每一个数对应相乘B的第j列每个数
每个相乘所得结果相加
最后放置于C矩阵的第i行第j号位
所以矩阵乘法中A的列数必须等于B的行数
struct matrix
{
lt mat[105][105],row,col;
matrix(int r=0,int c=0){
row=r; col=c;
for(int i=1;i<=row;++i)
for(int j=1;j<=col;++j)
mat[i][j]=0;
}
};
matrix operator *(matrix a,matrix b){
matrix c=matrix(a.row,b.col);
for(int i=1;i<=a.row;++i)
for(int j=1;j<=b.col;++j)
for(int k=1;k<=a.col;++k)
c.mat[i][j]+=a.mat[i][k]*b.mat[k][j]
return c;
}
时间复杂度为O(n^3)
虽然计算一次矩乘复杂度看似很大
但利用他却是可以将线性递推式优化成
O
(
l
o
g
n
)
O(logn)
O(logn)的!
因为矩阵乘法也是支持结合律的!!!
这是很重要的一点,因为这决定了他也同样可以快速幂
洛谷P3390 【模板】矩阵快速幂
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
typedef long long lt;
lt read()
{
lt f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const lt mod=1e9+7;
lt n;
struct matrix
{
lt mat[105][105],row,col;
matrix(int r=0,int c=0){
row=r; col=c;
for(int i=1;i<=row;++i)
for(int j=1;j<=col;++j)
mat[i][j]=0;
}
};
matrix operator *(matrix a,matrix b){
matrix c=matrix(a.row,b.col);
for(int i=1;i<=a.row;++i)
for(int j=1;j<=b.col;++j)
for(int k=1;k<=a.col;++k)
{
c.mat[i][j]+=a.mat[i][k]*b.mat[k][j]%mod;
c.mat[i][j]%=mod;
}
return c;
}
matrix qpow(matrix a,lt k)
{
matrix res=matrix(a.row,a.col);
for(int i=1;i<=a.row;++i) res.mat[i][i]=1;
while(k){
if(k&1) res=res*a;
a=a*a; k>>=1;
}
return res;
}
int main()
{
n=read(); lt k=read();
matrix a=matrix(n,n);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
a.mat[i][j]=read();
matrix ans=qpow(a,k);
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
printf("%d ",ans.mat[i][j]);
printf("\n");
}
return 0;
}
运用到实际题目中的话斐波那契数列计算就是很典型的例子
洛谷P1962 斐波那契数列【矩阵运算】
题目背景
大家都知道,斐波那契数列是满足如下性质的一个数列:
• f(1) = 1 f(2) = 1
• f(n) = f(n-1) + f(n-2) (n ≥ 2 且 n 为整数)
题目描述
请你求出 f(n) mod 1000000007 的值。
说明
对于 60% 的数据: n ≤ 92
对于 100% 的数据: n在long long(INT64)范围内。
题解分析:
这题主要的难点就在超大的数据范围
" n在long long(INT64)范围内。",直接递推什么的肯定是不行的
所以这时候我们的矩阵运算就派上用场啦
我们依次将斐波那契数放入矩阵
[
f
(
1
)
f
(
2
)
]
(1)
\begin{bmatrix} f(1) f(2) \end{bmatrix} \tag{1}
[f(1)f(2)](1)
[ f ( 3 ) f ( 4 ) ] (2) \begin{bmatrix} f(3) f(4) \end{bmatrix} \tag{2} [f(3)f(4)](2)
[ f ( 5 ) f ( 6 ) ] (3) \begin{bmatrix} f(5) f(6) \end{bmatrix} \tag{3} [f(5)f(6)](3)
我们试着用1矩阵乘以下面这个矩阵A
[
1
1
1
2
]
(A)
\begin{bmatrix} 1 & 1 \\ 1 & 2 \\ \end{bmatrix} \tag{A}
[1112](A)
发现得到了二号矩阵
再用二号矩阵去乘A矩阵,是不是又得到了三号?
由此我们只要对这个矩阵做快速幂就可以把时间复杂度降到log级
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
typedef long long lt;
lt read()
{
lt f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const lt mod=1e9+7;
lt n;
struct matrix
{
lt mat[5][5],row,col;
matrix(int r=0,int c=0){
row=r; col=c;
for(int i=1;i<=row;++i)
for(int j=1;j<=col;++j)
mat[i][j]=0;
}
};
matrix operator *(matrix a,matrix b){
matrix c=matrix(a.row,b.col);
for(int i=1;i<=a.row;++i)
for(int j=1;j<=b.col;++j)
for(int k=1;k<=a.col;++k)
{
c.mat[i][j]+=a.mat[i][k]*b.mat[k][j]%mod;
c.mat[i][j]%=mod;
}
return c;
}
matrix qpow(matrix a,lt k)
{
matrix res=matrix(a.row,a.col);
for(int i=1;i<=a.row;++i) res.mat[i][i]=1;
while(k){
if(k&1) res=res*a;
a=a*a; k>>=1;
}
return res;
}
int main()
{
n=read();
if(n==1||n==2){printf("1");return 0;}//记得特判
matrix fibo=matrix(1,2);
fibo.mat[1][1]=1; fibo.mat[1][2]=1;
matrix d=matrix(2,2);
d.mat[1][1]=1; d.mat[1][2]=1;
d.mat[2][1]=1; d.mat[2][2]=2;
//运算用的矩阵
lt k=n%2==0?n/2-1:n/2;
matrix tmp=qpow(d,k);
matrix ans=fibo*tmp;
if(n%2==1) printf("%lld",ans.mat[1][1]);
else printf("%lld",ans.mat[1][2]);
return 0;
}