• 矩阵小专题(矩阵快速幂+矩阵加速)


    1.什么是矩阵

    矩阵(数学术语)_百度百科


    2.矩阵快速幂

    首先要知道,只有n*n的矩阵能乘以自身(否则不符合矩阵相乘的条件)

    然后要明白普通的快速幂的原理(本质是把幂次二分,代码如下)

    1. inline int Pow(int a,int b){
    2. long long res=1, bs=a;
    3. while(b>0){if(b&1) res=res*bs%mod; bs=bs*bs%mod; b>>=1;}
    4. return res;
    5. }

    这里最终答案res从1开始,逐步让res乘以base值,以及让base值翻倍,从而达到了二分幂次的效果。

    换成矩阵,思路完全一样。但是要进行两个替换:

    1. 把 res=1 换成 res等于一个单位矩阵
    2. 数字相乘换成矩阵相乘,这一步可以用运算符重载来完成。

     附上模板题链接和代码:【模板】矩阵快速幂 - 洛谷

    1. #include
    2. using namespace std;
    3. #define FOR(i, a, b) for (int i = (a); i <= (b); i++)
    4. #define int long long
    5. #define pii pair
    6. const int N = 105, mod=1e9+7;
    7. int n, k; //矩阵大小,矩阵次方
    8. struct juzhen{
    9. int a[N][N];
    10. juzhen(){memset(a,0,sizeof(a));} //初始为0矩阵
    11. inline void build(){FOR(i,1,n) a[i][i]=1;} //建立单位矩阵
    12. void print(){
    13. FOR(i,1,n){
    14. FOR(j,1,n) cout<' ';
    15. cout<<'\n';
    16. }
    17. }
    18. juzhen operator*(const juzhen& b){ //重载矩阵乘法
    19. juzhen res;
    20. FOR(i,1,n) FOR(j,1,n){
    21. FOR(k,1,n) res.a[i][j] = (res.a[i][j] + this->a[i][k]*b.a[k][j]%mod)%mod;
    22. }
    23. return res;
    24. }
    25. }a;
    26. void solve(){
    27. cin>>n>>k;
    28. FOR(i,1,n) FOR(j,1,n) cin>>a.a[i][j];
    29. juzhen ans; ans.build(); //从一个单位矩阵开始
    30. while(k>0){ //矩阵快速幂计算
    31. if(k&1) ans=ans*a;
    32. a = a*a; k>>=1;
    33. }
    34. ans.print();
    35. }
    36. signed main(){
    37. ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    38. int T=1; //cin>>T;
    39. while(T--) solve();
    40. }

    3.矩阵加速 

    矩阵加速可以用来计算一些dp的计算,加速的原理就是前面说到的矩阵快速幂。

    1) 首先我们以一个简单的问题引入:斐波那契数列 - 洛谷

    这就是普通的斐波那契数列,但是n值达到了恐怖的2e9,显然原本O(n)的普通dp思路不可行。

    那么,我们就要用到矩阵加速了。

    考虑构造一个1*2的矩阵[f[i-1],f[i]],用这个矩阵进行递推。

    由斐波那契的递推式f[i] = f[i-2]+f[i-1]

    得到矩阵的递推式[f[i-1],f[i]] = [f[i-2],f[i-1]]*\begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix}

    再得到最终的计算式[f[n-1],f[n]] = [f[0],f[1]]*\begin{bmatrix} 0 & 1\\ 1 & 1 \end{bmatrix} ^{n-1}

    最终代码如下:

    1. #include
    2. using namespace std;
    3. #define FOR(i, a, b) for (int i = (a); i <= (b); i++)
    4. #define int long long
    5. #define pii pair
    6. const int mod = 1e9+7;
    7. int n;
    8. struct node{
    9. int a[3][3];
    10. node(){memset(a,0,sizeof(a));} //初始为0矩阵
    11. void build(){a[1][1]=0,a[1][2]=a[2][1]=a[2][2]=1;} //建立变换矩阵
    12. node operator*(const node&b){
    13. node res;
    14. FOR(i,1,2) FOR(j,1,2){
    15. FOR(k,1,2) res.a[i][j] = (res.a[i][j]+a[i][k]*b.a[k][j]%mod)%mod;
    16. }
    17. return res;
    18. }
    19. };
    20. node Pow(node a, int b){
    21. node res; res.a[1][1]=res.a[2][2]=1; //建立res为单位矩阵
    22. while(b>0){ //矩阵快速幂
    23. if(b&1) res=res*a;
    24. a=a*a; b>>=1;
    25. }
    26. return res;
    27. }
    28. pii cheng(pii a, node b){
    29. pii res;
    30. res.first = (a.first*b.a[1][1]%mod + a.second*b.a[2][1]%mod)%mod;
    31. res.second = (a.first*b.a[1][2]%mod + a.second*b.a[2][2]%mod)%mod;
    32. return res;
    33. }
    34. void solve(){
    35. cin>>n;
    36. node a; a.build();
    37. a = Pow(a,n-1);
    38. cout<<cheng({0,1},a).second<<'\n';
    39. }
    40. signed main(){
    41. ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    42. int T=1; //cin>>T;
    43. while(T--) solve();
    44. }

    2) 再推广到一般的矩阵加速,也是一样的做法,看下这道题:【模板】矩阵加速(数列) - 洛谷

    容易得到矩阵转移式为:[a,b,c]\rightarrow [b,c,a+c] 

    所以这次的转换矩阵就变成了\begin{bmatrix} 0 & 0 & 1\\ 1 & 0 & 0\\ 0 & 1 & 1 \end{bmatrix}

    最终代码如下:

    1. #include
    2. using namespace std;
    3. #define FOR(i, a, b) for (int i = (a); i <= (b); i++)
    4. #define int long long
    5. #define pii pair
    6. const int mod = 1e9+7;
    7. int n;
    8. struct node{
    9. int a[4][4];
    10. node(){memset(a,0,sizeof(a));} //初始为0矩阵
    11. void build(){a[1][3]=a[2][1]=a[3][2]=a[3][3]=1;} //建立变换矩阵
    12. node operator*(const node&b){
    13. node res;
    14. FOR(i,1,3) FOR(j,1,3){
    15. FOR(k,1,3) res.a[i][j] = (res.a[i][j]+a[i][k]*b.a[k][j]%mod)%mod;
    16. }
    17. return res;
    18. }
    19. void print(){
    20. FOR(i,1,3){
    21. FOR(j,1,3) cout<' ';
    22. cout<<'\n';
    23. }
    24. }
    25. };
    26. node Pow(node a, int b){
    27. node res; res.a[1][1]=res.a[2][2]=res.a[3][3]=1; //建立res为单位矩阵
    28. while(b>0){ //矩阵快速幂
    29. if(b&1) res=res*a;
    30. a=a*a; b>>=1;
    31. }
    32. return res;
    33. }
    34. struct tii{
    35. int a[4];
    36. tii(){memset(a,0,sizeof(a));}
    37. tii(int x,int y,int z){a[1]=x,a[2]=y,a[3]=z;}
    38. };
    39. tii cheng(tii a,node b){
    40. tii res;
    41. FOR(i,1,3){
    42. FOR(j,1,3) res.a[i] = (res.a[i] + a.a[j]*b.a[j][i]%mod)%mod;
    43. }
    44. return res;
    45. }
    46. void solve(){
    47. cin>>n;
    48. node a; a.build();
    49. a = Pow(a,n-1);
    50. cout<<cheng(tii(0,0,1),a).a[3]<<'\n';
    51. }
    52. signed main(){
    53. ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    54. int T=1; cin>>T;
    55. while(T--) solve();
    56. }

  • 相关阅读:
    OpenAI 超 700 名员工联名逼宫董事会;ChatGPT 新功能“阅后即焚”丨 RTE 开发者日报 Vol.89
    【Ubuntu】Systemctl控制nacos启动与关闭
    Spark基本知识
    5.django-模型ORM
    【爬虫实战】用python爬今日头条热榜TOP50榜单!
    主从数据一致性
    卷不动了,还卷吗?
    Metasploit工具你需要知道的功能
    java高级用法之:JNA中的Structure
    Notes原生应用Nomad上的表单设计
  • 原文地址:https://blog.csdn.net/bunny_1024/article/details/126250848