矩阵加速
【模板】矩阵加速(数列)
作用
有这样一种问题,要你求斐波那契数列的第$n$项,但是这个$n$可以大到$int64$的大小($2^{61}-1$),还会有若干次询问,这么多次询问还要你1s内出所有答案,传统的for
循环肯定GG,于是这时候就需要矩阵加速啦(利用矩阵运算+矩阵快速幂来加速递推求解过程)
- 那么这里就需要特别注意一点,那就是矩阵的左乘和右乘,这一点在快速幂函数中必须必须要注意,如果写反了就错了(按照个人习惯来,我比较习惯构造一个矩阵,然后让初始向量左乘它)
原理
对于模板题,我们可以很容易得到这样一个矩阵,它满足
于是,我们观察左侧向量的第一个数,初始为$a_1$,左乘一次矩阵得到$a_2$,所以左乘$n-1$次矩阵可以得到$a_n$,应用矩阵快速幂,打出如下代码
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #include<bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=1e9+7; struct M{ ll mat[15][15]; }a; M operator*(M x1,M x2){ M res; memset(res.mat,0,sizeof(res)); for(int i=1;i<=3;i++){ for(int j=1;j<=3;j++){ for(int k=1;k<=3;k++){ res.mat[i][j]+=(x1.mat[i][k]%mod*x2.mat[k][j]%mod)%mod; res.mat[i][j]%=mod; } } } return res; }
inline M qpow(M a,ll y,M ans){ M base=a; while(y>0){ if(y&1) ans=ans*base; y>>=1; base=base*base; } return ans; }
int main(){ int t; cin>>t; while(t--){ ll nn; cin>>nn; memset(a.mat,0,sizeof(a)); M b; memset(b.mat,0,sizeof(b)); b.mat[1][1]=b.mat[1][2]=b.mat[1][3]=1; a.mat[1][1]=a.mat[1][2]=a.mat[2][2]=a.mat[2][3]=a.mat[3][1]=0; a.mat[1][3]=a.mat[2][1]=a.mat[3][2]=a.mat[3][3]=1; M ans=qpow(a,nn-1,b); cout<<ans.mat[1][1]<<endl; } return 0; }
|
习题
观察得矩阵
AC代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #include<bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=1e9+7; struct M{ ll mat[15][15]; }a; M operator*(M x1,M x2){ M res; memset(res.mat,0,sizeof(res)); for(int i=1;i<=2;i++){ for(int j=1;j<=2;j++){ for(int k=1;k<=2;k++){ res.mat[i][j]+=(x1.mat[i][k]%mod*x2.mat[k][j]%mod)%mod; res.mat[i][j]%=mod; } } } return res; }
inline M qpow(M a,ll y,M ans){ M base=a; while(y>0){ if(y&1) ans=ans*base; y>>=1; base=base*base; } return ans; }
int main(){ ll nn; cin>>nn; memset(a.mat,0,sizeof(a)); M b; memset(b.mat,0,sizeof(b)); b.mat[1][1]=b.mat[1][2]=1; a.mat[1][1]=0; a.mat[1][2]=a.mat[2][1]=a.mat[2][2]=1; M ans=qpow(a,nn-1,b); cout<<ans.mat[1][1]<<endl; return 0; }
|
就是把系数改成了$p$和$q$而已,太简单了,矩阵转移如下
AC代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| #include<bits/stdc++.h> using namespace std; typedef long long ll; ll p,q,a1,a2,n,mod; struct M{ ll mat[15][15]; }a; M operator*(M x1,M x2){ M res; memset(res.mat,0,sizeof(res)); for(int i=1;i<=2;i++){ for(int j=1;j<=2;j++){ for(int k=1;k<=2;k++){ res.mat[i][j]+=(x1.mat[i][k]%mod*x2.mat[k][j]%mod)%mod; res.mat[i][j]%=mod; } } } return res; }
inline M qpow(M a,ll y,M ans){ M base=a; while(y>0){ if(y&1) ans=ans*base; y>>=1; base=base*base; } return ans; }
int main(){ cin>>p>>q>>a1>>a2>>n>>mod; memset(a.mat,0,sizeof(a)); M b; memset(b.mat,0,sizeof(b)); b.mat[1][1]=a1; b.mat[1][2]=a2; a.mat[1][1]=0; a.mat[1][2]=q; a.mat[2][1]=1; a.mat[2][2]=p; M ans=qpow(a,n-1,b); cout<<ans.mat[1][1]<<endl; return 0; }
|