设A矩阵是一个n*m的矩阵,B矩阵是一个m*p的矩阵,那么A与B相乘就得到n*p的C矩阵。
而Cij就等于A的第i行和B第j列对应的每个数相乘再相加的和。
只要满足第一个矩阵的列数与第二个矩阵的行数相同就行。
矩阵乘法有一些基本性质:
1、乘法结合律:(A*B)*C=A*(B*C)
2、乘法左右分配律:(A+B)*C=A*C+B*C或A*C+B*C=(A+B)*C
3、对称乘的结合律:k(A*B)=(k*A)*B=A*(k*B)
矩阵乘法一般不满足交换律。
矩阵乘法一个重要的作用就是加速递推公式,就拿斐波那契数列为例,给出前两项求第n项,那么可以先造一个1*2的A矩阵,分别放入第二项和第一项,再造出一个2*2的B矩阵,除了右下角是0,其他三个数都是1;那么将这两个矩阵相乘我们可以得到一个1*2的矩阵而这个矩阵的第一位就是f[1]+f[2],也就是f[3],第二位是f[2]。相当于把A矩阵往下推了一位。那么只要把A矩阵乘上n-2次B矩阵就能得到第n项了!
而把B矩阵乘上n-2次我们可以想到用快速幂,那么就说到了矩阵快速幂。
矩阵快速幂和常规快速幂差不多,只是把数与数相乘换成了矩阵与矩阵相乘,这里有两种方法:一是重载运算符,二是写一个矩阵乘法的函数(其实还可以暴力的把快速幂里的乘法都改成矩阵乘法)。
常规快速幂里的初始值为1,那么矩阵快速幂里的初始矩阵也应该具有1的性质(与任何矩阵相乘得到的矩阵是那个矩阵本身),而这个矩阵就是单位矩阵,单位矩阵是只有从左上到右下一条对角线为1,其他全部是0的正方形矩阵,可以发现这个矩阵乘任何矩阵都是那个矩阵本身。
定义完初始矩阵,我们还需要一个用来储存乘法结果的矩阵,值得注意的是只有当矩阵乘法全部乘完才能把这个矩阵的值付回去。
至于需要乘的B矩阵要根据题来自己创造,有的较难的题的递推式中会有常数。
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
//f[n]=a1*f[n-1]+a2*f[n-2]+...+ak*f[n-k];
int a[100];//系数
int f[100];//递推矩阵
int g[100][100];//转移矩阵
int s[100][100];//单位矩阵
int b[100];//前k项
int t[100][100];//结果矩阵
int q[100];//结果矩阵
int K;
int N;
int ans=0;
void quickpow(int g[][100],int n)
{
memset(s,0,sizeof(s));
memset(t,0,sizeof(t));
for(int i=1;i<=K;i++)
{
s[i][i]=1;
}
while(n)
{
if(n%2==1)
{
for(int i=1;i<=K;i++)
{
for(int j=1;j<=K;j++)
{
for(int k=1;k<=K;k++)
{
t[i][j]+=s[i][k]*g[k][j];
}
}
}
for(int i=1;i<=K;i++)
{
for(int j=1;j<=K;j++)
{
s[i][j]=t[i][j];
t[i][j]=0;
}
}
}
n=n/2;
for(int i=1;i<=K;i++)
{
for(int j=1;j<=K;j++)
{
for(int k=1;k<=K;k++)
{
t[i][j]+=g[i][k]*g[k][j];
}
}
}
for(int i=1;i<=K;i++)
{
for(int j=1;j<=K;j++)
{
g[i][j]=t[i][j];
t[i][j]=0;
}
}
}
}
int main()
{
scanf("%d",&K);
scanf("%d",&N);
memset(g,0,sizeof(g));
for(int i=1;i<=K;i++)
{
scanf("%d",&a[i]);
g[1][i]=a[i];
}
for(int i=1;i<=K;i++)
{
scanf("%d",&b[i]);
f[K-i+1]=b[i];
}
if(N<=K)
{
printf("%d",b[N]);
}
for(int i=2;i<=K;i++)
{
g[i][i-1]=1;
}
quickpow(g,N-K);
for(int i=1;i<=K;i++)
{
ans+=s[1][i]*f[i];
}
printf("%d",ans);
}