矩阵乘法是一种可以把递推优化到log(n)的一种算法。
矩阵乘法在运算的时候需要满足所相乘的两个矩阵的A的列数和矩阵B的行数相等时两个矩阵才可能想乘。比如一个矩阵A(2*3)和一个矩阵B(3*1)相乘可以得到一个矩阵C(2*1)的矩阵。
矩阵乘法在计算的时候满足结合律,但是不满足交换律,当我们在用矩阵乘进行计算的时候往往需要用到快速幂。
那么矩阵乘法的两项是如何运算的呢?
其实所得的矩阵的每一个位置i,j上的数就是矩阵A的第i行上的n个数和矩阵B的第j列上的n个数对应相乘再相加的和。
例如矩阵和相乘:
。
在了解了矩阵乘法的计算规则以后就可以去做题了,放一道矩阵乘法的基础题:
CODEVS1250 Fibonacci数列
定义:f0=f1=1, fn=fn-1+fn-2(n>=2)。{fi}称为Fibonacci数列。
输入n,求fn mod q。其中1<=q<=30000。
第一行一个数T(1<=T<=10000)。
以下T行,每行两个数,n,q(n<=109, 1<=q<=30000)
文件包含T行,每行对应一个答案。
3
6 2
7 3
7 11
1
0
10
1<=T<=10000
n<=109, 1<=q<=30000
这样我们所得到的矩阵就变成了
至于为什么要这么做呢,是因为我们要求的f[n]是由f[n-1]+f[n-2]得出的,所以我们需要一个矩阵B,这样就可以得到矩阵C应该和矩阵B一样的是f[n]和f[n-1],所以我们就可以知道矩阵A为什么了。
我们还可以将上面的式子再一次进行转化,就可以得出最后的答案就是
的首项了。至于前面的x^(n-2)的运算,我们就可以用快速幂了。最后的答案即为所得出的第一项。
至于第0和1项就特判一下就好了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define F(i,x,y) for(i=x;i<=y;++i)
using namespace std;
int a[2][2],ans[2][2],n,m,t,c[2][2],b[2];
int main()
{
scanf("%d",&t);
while(t--){
int i,j,out,y;
a[0][0]=ans[0][0]=1;a[0][1]=ans[0][1]=1;
a[1][0]=ans[1][0]=1;a[1][1]=ans[1][1]=0;
b[0]=b[1]=1;
scanf("%d%d",&n,&m);
if(n<=1) printf("1\n");
else{
y=n-2;
while(y){
if(y&1){
c[0][0]=(a[0][0]*ans[0][0]+a[0][1]*ans[1][0])%m;
c[0][1]=(a[0][0]*ans[0][1]+a[0][1]*ans[1][1])%m;
c[1][0]=(a[1][0]*ans[0][0]+a[1][1]*ans[1][0])%m;
c[1][1]=(a[1][0]*ans[0][1]+a[1][1]*ans[1][1])%m;
F(i,0,1)
F(j,0,1)
ans[i][j]=c[i][j];
}
y>>=1;
c[0][0]=(a[0][0]*a[0][0]+a[0][1]*a[1][0])%m;
c[0][1]=(a[0][0]*a[0][1]+a[0][1]*a[1][1])%m;
c[1][0]=(a[1][0]*a[0][0]+a[1][1]*a[1][0])%m;
c[1][1]=(a[1][0]*a[0][1]+a[1][1]*a[1][1])%m;
F(i,0,1)
F(j,0,1)
a[i][j]=c[i][j];
}
out=(ans[0][0]*b[0]+ans[0][1]*b[1])%m;
printf("%d\n",out);
}
}
}