矩阵乘法
矩阵乘法
设A为n∗k矩阵,B为k∗m矩阵,那么它们的乘积C则为一个n∗m矩阵
如图
矩阵乘法中满足结合律分配律,但不满足交换律
单位矩阵,都是正方形的,对角线上元素皆为1,其他皆为0,保证乘后矩阵不变
矩阵快速幂
同个矩阵相乘多次,形状不变,因此可以用快速幂计算,重载乘号即可(注意,答案先建成单位矩阵)
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
typedef long long ll ;
const int N=105;
const ll P=1e9+7;
using namespace std;
int n;ll k;
struct squ
{
ll a[N][N];
squ()
{
memset(a,0,sizeof(a));
}
inline void build() //建造单位矩阵,对角线为1,其他为0,乘上单位矩阵,结果不变
{
for(int i=1;i<=n;i++)a[i][i]=1;
}
}x;
squ operator *(const squ &x,const squ &y)//重载运算符
{
squ z;
for(int i=1;i<=n;i++)
for(int k=1;k<=n;k++)
for(int j=1;j<=n;j++)
z.a[i][k]=(z.a[i][k]+x.a[i][j]*y.a[j][k]%P)%P;//(i,k)=第i行*第k列
return z;
}
int main()
{
scanf("%d%lld",&n,&k);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%lld",&x.a[i][j]);
squ ans;
ans.build();
while(k)
{
if(k&1==1) ans=ans*x;
x=x*x;
k=k>>1;
}//递推快速幂,与普通的递推快速幂无异,但*不能缩写为*=
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
printf("%d ",ans.a[i][j]);
printf("\n");
}
return 0;
}
矩阵加速
通过构造矩阵,快速计算数列第n项
题目链接
要根据
可以乘上矩阵
因此要得到第n项,只需要用单位矩阵*初始矩阵的(n-1)次幂
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const ll P=1e9+7;
struct squ
{
ll a[5][5];
squ()
{
memset(a,0,sizeof(a));
}
};
squ operator *(const squ &x,const squ &y)
{
squ z;
for(int i=1;i<=3;i++)
for(int k=1;k<=3;k++)
for(int j=1;j<=3;j++)
z.a[i][k]=(z.a[i][k]+x.a[i][j]*y.a[j][k]%P)%P;
return z;
}
int main()
{
int T,x;
scanf("%d",&T);
for(int i=1;i<=T;i++)
{
scanf("%d",&x);
if(x<=3) printf("1\n");
else
{
squ ans,b;
for(int i=1;i<=3;i++) ans.a[i][i]=1;//建立单位矩阵
b.a[1][1]=b.a[1][3]=b.a[2][1]=b.a[3][2]=1;//建立初始矩阵
int k=x-1;
while(k)
{
if(k&1==1) ans=ans*b;
b=b*b;
k=k>>1;
}//x-1次方
printf("%lld\n",ans.a[1][1]);//第一列第一行即为答案
}
}
return 0;
}
斐波那契数列
递推式
初始矩阵
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const ll P=1e9+7;
struct squ
{
ll a[3][3];
squ()
{
memset(a,0,sizeof(a));
}
};
squ operator *(const squ &x,const squ &y)
{
squ z;
for(int i=1;i<=2;i++)
for(int k=1;k<=2;k++)
for(int j=1;j<=2;j++)
z.a[i][k]=(z.a[i][k]+x.a[i][j]*y.a[j][k]%P)%P;
return z;
}
int main()
{
ll n;
scanf("%lld",&n);
if(n<=2) printf("1");
else
{
squ ans,b;
for(int i=1;i<=2;i++) ans.a[i][i]=1;
b.a[1][1]=b.a[1][2]=b.a[2][1]=1;
ll k=n-1;
while(k)
{
if(k&1==1) ans=ans*b;
b=b*b;
k=k>>1;
}
printf("%lld\n",ans.a[1][1]%P);
}
return 0;
}
广义斐波那契
题目链接
递推式
初始矩阵
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
ll p,q,a1,a2,n,m;
struct squ
{
ll a[3][3];
squ()
{
memset(a,0,sizeof(a));
}
};
squ operator *(const squ &x,const squ &y)
{
squ z;
for(int i=1;i<=2;i++)
for(int k=1;k<=2;k++)
for(int j=1;j<=2;j++)
z.a[i][k]=(z.a[i][k]+x.a[i][j]*y.a[j][k]%m)%m;
return z;
}
int main()
{
scanf("%lld%lld%lld%lld%lld%lld",&p,&q,&a1,&a2,&n,&m);
if(n==1) printf("%lld",a1);
else if(n==2) printf("%lld",a2);
else
{
squ ans,b;
ans.a[1][1]=a2;ans.a[1][2]=a1;
b.a[1][1]=p,b.a[2][1]=q,b.a[1][2]=1;
ll k=n-2;
while(k)
{
if(k&1==1) ans=ans*b;
b=b*b;
k=k>>1;
}
printf("%lld\n",ans.a[1][1]);
}
return 0;
}