[LGOJ]P1939【模板】矩阵加速(数列)[矩阵加速递推]

题面

矩阵加速递推的原理:

首先你得会矩阵乘法与快速幂.

以斐波拉契数列为例,

要从矩阵A

\[ \begin{bmatrix} f[n-1] & f[n]  \end{bmatrix} \]

得到矩阵B

\[ \begin{bmatrix} f[n] & f[n+1]  \end{bmatrix} \]

显然可以\[\begin{bmatrix} f[n-1] & f[n] \end{bmatrix}\times \begin{bmatrix}  0 & 1\\ 1 & 1 \end{bmatrix}=\begin{bmatrix} f[n] & f[n-1]+f[n] \end{bmatrix}=\begin{bmatrix} f[n] & f[n+1] \end{bmatrix}\]

那么要求数列第n项,

用初始矩阵

\[ \begin{bmatrix}  1 & 1 \end{bmatrix}\]

乘上

\[{ \begin{bmatrix}  0 & 1 \\ 1 & 1 \end{bmatrix} }^{n-1}\]

对于这道题,

$ f[1]=f[2]=f[3]=1$

$ f[x]=f[x-3]+f[x-1]$

显然从一个状态推至下一状态要保存3个连续的元素, 因为关系式$ f[x]=f[x-3]+f[x-1]$一共跨过了3个元素, 于是两个矩阵就出来了:

\[A=\begin{bmatrix}  f[n-2] & f[n-1] & f[n] \end{bmatrix}\]

\[C=\begin{bmatrix}  f[n-1] & f[n] & f[n+1] \end{bmatrix}\]

那么怎样从A转移到下一状态C呢

我们来构造一个矩阵B

先考虑C的第一个元素, $ f[n-1]$ ,直接从A的第二个转移来即可,所以B的第一列为0 1 0

考虑C的第2个元素,$ f[n]$ ,直接从A的第3个转移来即可,所以B的第2列为0 0 1

考虑C的第3个元素,$ f[n+1]$ ,跟据递推式,从A的第1个和第3个相加转移来即可, 所以B的第3列为1 0 1

综上,

\[B=\begin{bmatrix}  0 & 0 & 1\\ 1 & 0 & 0 \\ 0 & 1 & 1\end{bmatrix}\]

\[A\times B = C\]

\[\begin{bmatrix}  f[n-2] & f[n-1] & f[n] \end{bmatrix}\times\begin{bmatrix}  0 & 0 & 1\\ 1 & 0 & 0 \\ 0 & 1 & 1\end{bmatrix} \]

\[= \begin{bmatrix}  f[n-1] & f[n] & f[n-2]+f[n] \end{bmatrix}\]

\[ = \begin{bmatrix}  f[n-1] & f[n] & f[n+1] \end{bmatrix}\]

通过矩阵乘法实现了状态的转移,接下来通过快速幂加速即可.

对了, 我写的快速幂是非递归版, 可能理解方式也不一样, 具体见代码里面快速幂部分的注释

另外, 为了方便处理, 程序中统一把矩阵变为正方形的, 原来没有元素的地方赋值为0即可

#include<cstdio>
#include<cstring>
#define re register
#define in inline
#define int long long
inline int read()
{
    int s=0,b=1;
    char ch;
    do{
        ch=getchar();
        if(ch==‘-‘) b=-1;
    }while(ch<‘0‘ || ch>‘9‘);
    while(ch>=‘0‘ && ch<=‘9‘)
    {
        s=s*10+ch-‘0‘;
        ch=getchar();
    }
    return b*s;
}//快读是不可能出锅的
int size=3,n,T;
const int mod=1000000007;
struct jz{
    int v[5][5];
    void ql()
        {
            memset(v,0,sizeof(v));
        }//矩阵清零
    void csh()
        {
            ql();
            for(re int i=1;i<=size;++i)
                v[i][i]=1;
        }//赋值为单位矩阵
    jz operator *(const jz &t)const
        {
            jz c;
            c.ql();
            for(re int i=1;i<=size;++i)
                for(re int j=1;j<=size;++j)
                    for(re int k=1;k<=size;++k)
                        c.v[i][j]=c.v[i][j]%mod+v[i][k]*t.v[k][j]%mod;
            return c;
        }//矩阵乘法
};
jz ksm(jz a,int p)//矩阵快速幂
{
    /*
      非递归快速幂思想:
      假设p=11,即二进制1011
      则p=2^3+2^2+2^0
      那么a^p转化为a^2^3*a^2^2*a^2^0
      a^2^x即base,a^2^(x+1)可以很快由a^2^(x)乘a得到
      只要p的二进值末尾为1, 就乘入ans即可
    */
    jz ans,base=a;//base一开始为a^2^0即a
    ans.csh();//注意ans要初始化为单位矩阵
    while(p)
    {
        if(p&1) ans=ans*base; //如果p的二进制末尾为1,ans就要乘上a^2^x. 这里别去想什么奇数偶数的, 直接看p的二进制
        base=base*base; //base由a^2^x转为a^2^(x+1)
        p>>=1; //p的末尾已处理完(a^2^x已乘入ans)
    }
    return ans;
}
signed main()
{
    jz st,zy;
    st.ql();
    zy.ql();
    st.v[1][1]=1;st.v[1][2]=1;st.v[1][3]=1;
    //初始矩阵st为|1 1 1|
    zy.v[1][1]=0;zy.v[1][2]=0;zy.v[1][3]=1;
    zy.v[2][1]=1;zy.v[2][2]=0;zy.v[2][3]=0;
    zy.v[3][1]=0;zy.v[3][2]=1;zy.v[3][3]=1;
    /*
      转移矩阵为
      |0 0 1|
      |1 0 0|
      |0 1 1|
      f[n]~f[n+1]转移过程:
      |f[n-2] f[n-1] f[n]| * |0 0 1| = |f[n-1] f[n] f[n-2]+f[n]| =|f[n-1] f[n] f[n+1]|
                             |1 0 0|
                             |0 1 1|
    */
    T=read();
    for(re int i=1;i<=T;++i)
    {
        n=read();
        jz ans=st*ksm(zy,n-1);//转移次数玄学调一下就好.这里st*zy^(n-1)得到[1][1]为答案
        printf("%lld\n",ans.v[1][1]%mod);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/cgazn/p/10327641.html

时间: 2024-10-10 22:56:22

[LGOJ]P1939【模板】矩阵加速(数列)[矩阵加速递推]的相关文章

HDU 5863 cjj&#39;s string game ( 16年多校10 G 题、矩阵快速幂优化线性递推DP )

题目链接 题意 : 有种不同的字符,每种字符有无限个,要求用这k种字符构造两个长度为n的字符串a和b,使得a串和b串的最长公共部分长度恰为m,问方案数 分析 : 直觉是DP 不过当时看到 n 很大.但是 m 很小的时候 发现此题DP并不合适.于是想可能是某种组合数学的问题可以直接公式算 看到题解的我.恍然大悟.对于这种数据.可以考虑一下矩阵快速幂优化的DP 首先要想到线性递推的 DP 式子 最直观的想法就是 dp[i][j] = 到第 i 个位置为止.前面最长匹配长度为 j 的方案数 但是如果仔

矩阵快速幂优化线性递推

我们熟知的斐波那契数列递推公式是: \(f(n)=f(n-1)+f(n-2)\) 假设我们需要求斐波那契数列的第n项,当n非常大(如n=1e9)的时候,递推肯定超时.我们不妨设: \(\binom{f_{n}}{f_{n-1}}=\begin{pmatrix}a & b\\ c & d\end{pmatrix}\binom{f_{n-1}}{f_{n-2}}\) 将等式右边乘开,得到: \(\binom{af_{n-1}+bf_{n-2}}{cf_{n-1}+df_{n-2}}\) 要使其

HDU - 2604 Queuing(矩阵快速幂或直接递推)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2604 题意:给出字符串长度L,并且字符串只由'f','m'构成,有2^L种情况,问在其中不包含'fmf','fff'的字符串有多少个. 1.直接递推,虽然过了,但是数据稍微大点就很可能TLE,因为代码上交上去耗时还是比较长的(感觉数据有点水)╭(′▽`)╭(′▽`)╯( 1 #include <iostream> 2 #include <cstdio> 3 #include <c

BZOJ-2431: [HAOI2009]逆序对数列 (傻逼递推)

2431: [HAOI2009]逆序对数列 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 2401  Solved: 1389[Submit][Status][Discuss] Description 对于一个数列{ai},如果有i<j且ai>aj,那么我们称ai与aj为一对逆序对数.若对于任意一个由1~n自然数组成的 数列,可以很容易求出有多少个逆序对数.那么逆序对数为k的这样自然数数列到底有多少个? Input 第一行为两个整数n,k. Ou

剑指offer-矩形覆盖-斐波那契数列(递归,递推)

class Solution { public: int rectCover(int number) { if(number==0 || number==1||number==2) return number; return rectCover(number-1)+rectCover(number-2); } }; *******************************************************************************************

斐波那契数列 递归 尾递归 递推 C++实现

==================================声明================================== 本文原创,转载请注明作者和出处,并保证文章的完整性(包括本声明). 本文不定期修改完善,为保证内容正确,建议移步原文处阅读. 本文链接:http://www.cnblogs.com/wlsandwho/p/4205524.html ===============================================================

斐波那契数列数组递归递推的时间空间复杂度的分析

3g0gpapcgj傅节涤澳浩日换崭睹速<http://weibo.com/p/230927987595257304584192> nfdc5g6tss谅渍狙庇淤律臼忠圆账<http://weibo.com/p/230927987596255930617856> 2kvbhyaba6棠妨睦阶月忧杜懊猎茄<http://weibo.com/p/230927987595884084596736> iw27choola戮鸵垦呀粟傧段厍刀推<http://weibo.co

P1356 数列的整除性 递推,丝毫没dp的感觉

题意:给出一串数字,让我们在其中加 + - 号,能加n-1个,正负号任意组合 只要其中一个结果能整除k,就输出 可以 全部结果都不能整除,就输出不可以 思路:题意给出的数据范围为n(1e4) k(1e2 ) 即要除的数只有100这么大,那么每一次枚举,我们显然可以枚举1到100内的数 只要在当前这个数存在,我们就可以继续枚举 那么复杂度就是100*1e4  可行 代码如下: 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int

矩阵快速幂 ——(递推表达式)

矩阵快速幂 首先知道矩阵 矩阵(Matrix)是一个按照长方阵列排列的复数或实数集合: 矩阵乘法: 定义:设A为 m×p 的矩阵,B为 p×n 的矩阵,那么称 m×n 的矩阵C为矩阵A与B的乘积,记作 C=A×B ,其中矩阵C中的第 i 行第 j 列元素可以表示为: 知道矩阵乘法之后,比如菲波那切数列就是一个递推式, F(n)=F(n-1)+F(n-2); 因为矩阵乘法,所以 设 矩阵 A为 矩阵 B 为 则  A*B  则为    F(n)=        F(n-1)*1+F(n-2)*1

2017中国大学生程序设计竞赛 - 女生专场 Happy Necklace(递推+矩阵快速幂)

Happy Necklace Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 1146    Accepted Submission(s): 491 Problem Description Little Q wants to buy a necklace for his girlfriend. Necklaces are single