矩阵快速幂入门笔记

矩阵乘法基本运算

这里写图片描述

如图,矩阵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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值