zoukankan      html  css  js  c++  java
  • Luogu P3390 【模板】矩阵快速幂&&P1939 【模板】矩阵加速(数列)

    补一补之前的坑

    因为上次关于矩阵的那篇blog写的内容太多太宽泛了,所以这次把一些板子和基本思路理一理

    先看这道模板题:P3390 【模板】矩阵快速幂

    首先我们知道矩阵乘法满足结合律而不满足交换律的一种运算

    因此我们对于矩阵A的p次只需要先算出A^(p/2)即可

    这不就是快速幂吗,快速幂的模板看这里

    然后我们把其中的整数乘法改成矩阵乘法即可

    关于矩阵的其他东西都不会,好吧,看一看概述矩阵

    CODE

    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long LL;
    const int N=105,mod=1e9+7;
    inline char tc(void)
    {
        static char fl[100000],*A=fl,*B=fl;
        return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(LL &x)
    {
        x=0; char ch=tc();
        while (ch<'0'||ch>'9') ch=tc();
        while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
    }
    inline void write(int x)
    {
        if (x/10) write(x/10);
        putchar(x%10+'0');
    }
    struct Matrix
    {
        int n,m;
        LL a[N][N];
        inline void input(void)
        {
            for (register int i=1;i<=n;++i)
            for (register int j=1;j<=m;++j)
            read(a[i][j]);
        }
        inline void output(void)
        {
            for (register int i=1;i<=n;++i,putchar('
    '))
            for (register int j=1;j<=m;++j)
            write(a[i][j]),putchar(' ');
        }
        inline void cri_init(void)
        {
            memset(a,0,sizeof(a));
            for (register int i=1;i<=n;++i)
            a[i][i]=1;
        }
    };
    LL k,n;
    inline Matrix mul(Matrix A,Matrix B)
    {
        Matrix C; C.n=A.n; C.m=B.m;
        memset(C.a,0,sizeof(C.a));
        for (register int i=1;i<=C.n;++i)
        for (register int j=1;j<=C.m;++j)
        for (register int k=1;k<=A.m;++k)
        C.a[i][j]=(C.a[i][j]+A.a[i][k]*B.a[k][j])%mod;
        return C;
    }
    inline Matrix quick_pow(Matrix A,LL p)
    {
        Matrix T; T.n=T.m=n; T.cri_init();
        while (p)
        {
            if (p&1) T=mul(T,A);
            A=mul(A,A); p>>=1;
        }
        return T;
    }
    int main()
    {
        //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); 
        read(n); read(k);
        Matrix A; A.n=A.m=n;
        A.input();
        A=quick_pow(A,k);
        A.output();
        return 0;
    }
    

    再看这道题:P1939 【模板】矩阵加速(数列)

    主要讲一下矩阵与递推之间如何转化

    首先我们看题目给出的式子:

    • a[1]=a[2]=a[3]=1

    • a[x]=a[x-3]+a[x-1] (x>3)

    首先我们通过题目给出的初始值得到初始的列向量

    1 a[1]
    1 分别表示 a[2]
    1 a[3]

    我们发现,当前的这一项与它的前三项都有关,因此我们可以建立一个3*3的矩阵

    然后因为a[4]=a[1]+a[3],而稍加推导可以将a[2]代替a[1]的位置,a[3]代替a[2]的位置

    注意这里就很重要了,因为a[1]对于a[5]以及以后的推导没有任何作用了,因此可以直接被覆盖

    可以结合滚动数组的思想进行一下理解

    然后我们得出递推矩阵:

    0 1 0
    0 0 1
    1 0 1

    手推一下就会发现刚好完成了想要的效果

    然后我们只需要把初始的列向量乘递推矩阵(n-3)次即可

    矩阵快速幂求之

    CODE

    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long LL;
    const int N=4,mod=1e9+7;
    struct Matrix
    {
        int n,m;
        LL a[N][N];
        inline void Dt_init(void)
        {
            n=m=3; memset(a,0,sizeof(a));
            a[1][2]=a[2][3]=a[3][1]=a[3][3]=1;
        }
        inline void cri_init(void)
        {
            n=m=3; memset(a,0,sizeof(a));
            for (register int i=1;i<=n;++i)
            a[i][i]=1;
        }
    };
    int t,n;
    inline char tc(void)
    {
        static char fl[100000],*A=fl,*B=fl;
        return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
        x=0; char ch=tc();
        while (ch<'0'||ch>'9') ch=tc();
        while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
    }
    inline void write(int x)
    {
        if (x/10) write(x/10);
        putchar(x%10+'0');
    }
    inline Matrix mul(Matrix A,Matrix B)
    {
        Matrix C; C.n=A.n; C.m=B.m;
        memset(C.a,0,sizeof(C.a));
        for (register int i=1;i<=C.n;++i)
        for (register int j=1;j<=C.m;++j)
        for (register int k=1;k<=A.m;++k)
        C.a[i][j]=(C.a[i][j]+A.a[i][k]*B.a[k][j])%mod;
        return C;
    }
    inline Matrix quick_pow(Matrix A,int p)
    {
        Matrix T; T.cri_init();
        while (p)
        {
            if (p&1) T=mul(T,A);
            A=mul(A,A); p>>=1;
        }
        return T;
    }
    int main()
    {
        //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); 
        read(t);
        while (t--)
        {
            read(n); Matrix F; F.Dt_init();
            if (n<=3) { puts("1"); continue; }
            F=quick_pow(F,n-3);
            write((F.a[3][1]+F.a[3][2]+F.a[3][3])%mod); putchar('
    ');
        }
        return 0;
    }
    

    最后我们简单总结一下用矩阵乘法优化递推的步骤:

    1. 通过题目给出的关系得出线性递推关系

    2. 列出初始矩阵的值,通常根据初始条件确定

    3. 通过递推式,得到每一项的关系由那些地方转移过来,一般来说,就可以吧推得的当前项的项在矩阵中的位置附上1(如果有乘的关系就赋成负数),但具体还是根据题目意思而定

    4. 通过矩阵快速幂来优化乘法,得到最终矩阵并与初始矩阵相乘

    然后就静候AC吧

  • 相关阅读:
    Java 基础入门随笔(6) JavaSE版——数组操作
    Java 基础入门随笔(5) JavaSE版——函数重载
    Java 基础入门随笔(4) JavaSE版——程序流程控制
    Java 基础入门随笔(3) JavaSE版——逻辑运算符、位运算符
    jvm第二章(二)
    jvm第二章(一)
    jvm第一章(三)
    jvm第一章(二)
    jvm第一章(一)
    Java程序初始化的顺序是怎样的?
  • 原文地址:https://www.cnblogs.com/cjjsb/p/9047769.html
Copyright © 2011-2022 走看看