首先关于矩阵的一些基本常识在这篇博客里已经介绍的很清楚了,在这里不在赘述。
但是矩阵乘的图片是一定放的,因为比较重要好像只有矩阵乘,。
观察图片我们可以发现,对于一个新矩阵的 \(F[i][j]\) 其实就是第一个矩阵的第 \(i\) 行和对应第二个矩阵的 \(j\) 列的每一个数乘起来再相加。这也就解释了为什么只有大小为 \(n*m\) 和 \(m*k\) 的矩阵才可以相乘,且新矩阵为大小为 \(n*k\) ,如果不是这样,就会出现对不上项的情况。
和实数一样,在定义了矩阵乘之后矩阵幂也就自然出来了,只有 \(n*n\) 的矩阵才可以不断连乘。
就像上面的新矩阵 \(n*k\) ,如果 \(n\) \(m\) \(k\) 不相等,那么就会发现没有旧矩阵和新矩阵可以不断相乘。
还有就是单位矩阵,任何矩阵乘这个矩阵都是其本身,就像实数里的 \(1\) 一样,定义上面的博文里已经很详细了,这里只是口糊一下,就是只有对角线元素是 \(1\) 其余元素都为 \(0\) 的矩阵。
模板题,直接放代码
#include<bits/stdc++.h>
#define ll long long
const int N=110;
const ll MOD=1e9+7;
ll n,k;
struct Matrix{
int n;
ll a[N][N];
inline void clear(int size)
{
n=size;
memset(a,0,sizeof(a));
return ;
}
inline void build(int size)
{
n=size;
memset(a,0,sizeof(a));
for (int i=1;i<=n;i++)
a[i][i]=1;
return ;
}
inline void out()
{
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
printf("%lld ",a[i][j]);
printf("\n");
}
}
};
inline Matrix operator * (Matrix a,Matrix b)
{
Matrix c;
c.clear(a.n);
for (int k=1;k<=c.n;k++)
for (int i=1;i<=c.n;i++)
for (int j=1;j<=c.n;j++)
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j]%MOD)%MOD;
return c;
}
inline Matrix Matrix_qpow(Matrix a,ll p)
{
Matrix ans,base=a;
ans.build(a.n);
for (;p;p>>=1,base=base*base)
if (p&1) ans=ans*base;
return ans;
}
int main()
{
scanf("%lld%lld",&n,&k);
Matrix now;
now.clear(n);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
scanf("%lld",&now.a[i][j]);
Matrix_qpow(now,k).out();
return 0;
}
需要注意的一个问题是如果存矩阵的结构体占用空间过大,当其传入自定义函数的时候会引起爆栈。
可以在编译命令里加入无限栈空间的命令。
-Wl,--stack=1234567890
那么说了那么多,矩阵乘和矩阵快速幂有什么用呢??
矩阵快速幂可以优化一些递推式,使 \(\Theta(n)\) 的时间复杂度变为 \(\Theta (\log_2n)\) 。
下面的题目就是一个比较简单的应用。
思路主要来自这篇博客 ,这里主要就是口述一下过程。
首先我们知道斐波那契数列 \(F[n]=F[n-1]+F[n-2]\) ,对于 \(F[n]\) 这和 \(F[n-1]\) 和 \(F[n-2]\) 有关,我们设 \(Fib[n-1]\) 为一个 \(F[n-1]\) 和 \(F[n-2]\) 的二元组,所以 \(Fib[n]\) 就是 \(F[n]\) 和 \(F[n-1]\) ,考虑 \(Fib[n]\) 怎样由 \(Fib[n-1]\) 得到。我们发现
\[F[n]=F[n-1]*1+F[n-2]*1\]
\[F[n-1]=F[n-1]*1+F[n-2]*0\]
如果放了矩阵上,它就是这样的。
然后我们发现求第 \(n\) 项的过程其实就是不断进行上面的过程的过程,最终的答案就是求下面这个矩阵的第一行第一个元素
\[\left[\begin{array}{cc}{F_{n-1}} & {F_{n-2}}\end{array}\right] \times\left[\begin{array}{cc}{1} & {1} \\ {1} & {0}\end{array}\right]^{n-2}\]
因为矩阵满足结合律,所以我们就先用矩阵快速幂求出来\(\left[\begin{array}{ll}{1} & {1} \\ {1} & {0}\end{array}\right]^{n-2}\) ,然后再求 \(\left.[\begin{array}{ll}{1} & {1}\end{array}\right] \times\left[\begin{array}{ll}{1} & {1} \\ {1} & {0}\end{array}\right]^{n-2}\) ,需要注意的是矩阵并不满足交换律,所以不能换过来乘。
和上面的那一道题目推法差不多,一般来说要求的项数前与 \(n\) 项相关,矩阵就是 \(n*n\) 的。
#include<bits/stdc++.h>
#define ll long long
const int N=12;
const ll MOD=1e9+7;
struct Matrix{
int n;
ll a[N][N];
inline void clear(int sz)
{
n=sz;
memset(a,0,sizeof(a));
return ;
}
inline void build(int sz)
{
n=sz;
memset(a,0,sizeof(a));
for (int i=1;i<=n;i++) a[i][i]=1;
return ;
}
inline void out()
{
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
printf("%lld ",a[i][j]);
printf("\n");
}
return ;
}
};
inline Matrix operator * (Matrix a,Matrix b)
{
Matrix c;
c.clear(a.n);
for (int k=1;k<=c.n;k++)
for (int i=1;i<=c.n;i++)
for (int j=1;j<=c.n;j++)
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j]%MOD)%MOD;
return c;
}
inline Matrix Matrix_qpow(Matrix a,ll p)
{
Matrix ans,base=a;
ans.build(a.n);
for (;p;p>>=1,base=base*base)
if (p&1) ans=ans*base;
return ans;
}
int t;
int main()
{
scanf("%d",&t);
while (t--)
{
ll k;
scanf("%lld",&k);
if (k<=3)
{
printf("1\n");
continue;
}
Matrix now,ans;
now.clear(3);
now.a[1][1]=1;now.a[1][2]=1;now.a[1][3]=0;
now.a[2][1]=0;now.a[2][2]=0;now.a[2][3]=1;
now.a[3][1]=1;now.a[3][2]=0;now.a[3][3]=0;
ans=Matrix_qpow(now,k-3);
printf("%lld\n",(ans.a[1][1]+ans.a[2][1]+ans.a[3][1])%MOD);
}
return 0;
}
不过在最后乘的时候一定要严格按照矩阵乘法的定义来,因为你永远不知道经过矩阵快速幂的矩阵是什么。
只是加了系数,和上面的推法一样,需要注意的是定义的递推矩阵和给的顺序正好相反。
#include<bits/stdc++.h>
#define ll long long
const int N=5;
struct Matrix{
int n;
ll a[N][N];
inline void clear(int sz)
{
n=sz;
memset(a,0,sizeof(a));
return ;
}
inline void build(int sz)
{
n=sz;
memset(a,0,sizeof(a));
for (int i=1;i<=n;i++)
a[i][i]=1;
return ;
}
};
ll q,p,a1,a2,n,mod;
inline Matrix operator * (Matrix a,Matrix b)
{
Matrix c;
c.clear(a.n);
for (int k=1;k<=c.n;k++)
for (int i=1;i<=c.n;i++)
for (int j=1;j<=c.n;j++)
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j]%mod)%mod;
return c;
}
inline Matrix Matrix_qpow(Matrix a,ll p)
{
Matrix ans,base=a;
ans.build(a.n);
for (;p;p>>=1,base=base*base)
if (p&1) ans=ans*base;
return ans;
}
int main()
{
scanf("%lld%lld%lld%lld%lld%lld",&p,&q,&a1,&a2,&n,&mod);
Matrix now,ans;
now.clear(2);
now.a[1][1]=p;now.a[1][2]=1;
now.a[2][1]=q;now.a[2][2]=0;
ans=Matrix_qpow(now,n-2);
printf("%lld\n",(ans.a[1][1]*a2%mod+ans.a[2][1]*a1%mod)%mod);
return 0;
}