【总结】 矩阵快速幂
前言
如果有不清楚矩阵乘法的,可以加QQ了解一下:3099895260
如果会的话可以跳过这个步骤
就是我们设两个矩阵为\(a\),\(b\)。他们两个可以乘在一起当且仅当第一个矩阵为\(n*p\)且第二个矩阵\(p*m\),他们乘起来的结果是\(c\)矩阵,\(n*m\)
然后乘法就是这样子实现的.
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=p;k++)
c[i][j]=a[i][k]*b[k][j];
然后如果有多个同样子的矩阵乘在一起,就可以像普通乘法快速幂。
正话
矩阵快速幂的板子基本上是没有用的,真正在题目中出现的还是矩阵加速。
我们先来看一道题目:矩阵加速(数列)
这显然是递推对吧,但是因为\(n<=2*10^9\)十分无奈,所以考虑优化。
我们来深层次挖掘一下他的式子:
\(a_i=a_{i-1}+a_{i-3}(i>3)\)
这个东西有什么用呢?
我们令一个矩阵为\(S\{3*1\}={a_i,a_{i-1},a_{i-2}}\)
那么如果我们要递推呢?
如何转移到\(a_{i+1},a_i,a_{i-1}\)呢?
再令一个矩阵\(T\{3*3\}\)表示一个转移矩阵,那么根据递推式子和矩阵乘法的原则,有一个这样子的矩阵?
\(T[3][3]={1,1,0},{0,0,1},{1,0,0}\)这个可能竖着比较好看。
\(\{1,1,0\}\)
\(\{0,0,1\}\)
\(\{1,0,0\}\)
那么就是每一列对应着对应位置的转移,大致推导就是这样。
那么考虑简化这个东西。
\(Ans=(((S*T)*T)*T)\)
那么可以把括号拆开,就可以得出\(Ans=S*T^k\)
所以可以矩阵快速幂优化就可以AC!!!
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<queue>
#include<algorithm>
#define ll long long
#define re register
inline int gi(){
int f=1,sum=0;char ch=getchar();
while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return f*sum;
}
const ll Mod=1e9+7;
struct matrix{
ll a[3][3];
matrix operator*(const matrix b)const{
matrix c;
for(re int i=0;i<3;i++)
for(re int j=0;j<3;j++){
c.a[i][j]=0;
for(re int k=0;k<3;k++)
c.a[i][j]=(c.a[i][j]+a[i][k]*b.a[k][j]%Mod)%Mod;
}
return c;
}
}S,T;
void init(){
S.a[0][0]=S.a[0][1]=S.a[0][2]=1;
T.a[0][0]=T.a[0][1]=T.a[2][0]=1;
T.a[1][1]=T.a[1][0]=T.a[0][2]=T.a[2][1]=T.a[2][2]=0;
T.a[1][2]=1;
}
int n;
int main(){
int t=gi();
while(t--){
n=gi();
if(n<=2){
puts("1");continue;
}
init();
n-=3;
while(n){
if(n&1)S=S*T;
T=T*T;n>>=1;
}
printf("%lld\n",S.a[0][0]);
}
return 0;
}