题解 P1939 【【模板】矩阵加速(数列)】 矩阵快速幂

题目 

传送门

其实这一题还是挺简单的(如果你会矩阵快速幂的话),不会的快去学啊!!! 好啦,回归正题,下面就讲一下本蒟蒻的拙见。


原理部分

由题知: - f[n]=f[n-1]+f[n-3]。我们只要从这个公式出发就好了。 - 我们假设有两个数组A[],B[]。

 -   A:f[3],f[2],f[1]。//A只有一行三列。
  • B: //B需要手推。

        1 1 0
        
        0 0 1
        
        1 0 0
    

-如果我们让AB会有什么神奇的事发生呢? -聪明的你一定发现了 - AB=(f[3]+f[1]+0,f[3]+0+0,f[2]+0+0) =f[4],f[3],f[2] - 这时候我们把得到的结果再乘一个B数组 - 它就神奇的变为 f[5],f[4],f[3]啦! - 我们再乘,每个f的下表就加1。 于是,我们得到结论。

(f[n],f[n-1],f[n-2])*B=(f[n+1],f[n],f[n-1])。

我们最开始知道 f[3],f[2],f[1],把它当做A数组。
如果我们想知道f[x]的值,我们不是只要乘x-3个B就能得到答案了么。

嗯(too young too sample,哈哈),但我用递推也是推x-3次, 那我干嘛还要用矩阵加速呢?

别慌,我有矩阵快速幂!!!

虽然矩阵乘没有交换率(即A×B≠B×A),

但它有结合率啊(即(A×B)×C==A×(B×C))。

既然我们有x-3个B要乘,那我们可以先把它用快速幂求出来。

(什么,你不会快速幂,快去学啊。)

即求B^(x-3)。最后再乘A。

就可以通过减少相乘的次数来加快时间。

是不是恍然大悟了呢!

代码部分

(略丑,别怕。)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define ll long long
ll p=1000000007;
struct node{
    ll map[4][4];
}e,ans;
ll t,temp,answer=0;
node mul(node a,node b)//用来两个矩阵相乘的函数
{
    node tp;
    memset(tp.map,0,sizeof(tp));
    for(int i=1;i<=3;i++)
    for(int j=1;j<=3;j++)
    for(int z=1;z<=3;z++)
    tp.map[i][j]=(tp.map[i][j]+a.map[i][z]*b.map[z][j]%p)%p;
    return tp;
}
void quick (node a,ll b)//矩阵快速幂。
{
    memset(ans.map,0,sizeof(ans));
    ans.map[1][1]=ans.map[2][2]=ans.map[3][3]=1;//设置单位矩阵,相当
                                               //与普通快速幂里的ans 
                                               //用来记录答案。
    while(b)
    {
    if(b&1)
    ans=mul(ans,a);
    a=mul(a,a);
    b=(b>>1);
   }
}
int main()
{
    memset(e.map,0,sizeof(e));
    e.map[1][1]=e.map[1][2]=e.map[2][3]=e.map[3][1]=1;//B数组的初值。
    scanf("%lld",&t);
    for(int i=1;i<=t;i++)
    {
      scanf("%lld",&temp);
      if(temp==1||temp==2||temp==3)//如果是求f[1],f[2],f[3]直接输出
        {
            printf("%d\n",1);
            continue;
        }
        quick(e,temp-3);//求B^(x-3)。
    printf("%lld\n",(ans.map[1][1]+ans.map[2][1]+ans.map[3][1])%p);
//最后的A*B^(x-3)。由于只要f[x],我们手动相乘,不再操作f[x-1],f[x-2]。
    }
return 0;
}

 

好啦,如果您有哪里不懂的话并且不嫌弃本蒟蒻的话,可以私信我哦。

有哪里讲的不对的话dalao一定指出来哦,望dalao斧正。

posted @ 2020-07-29 18:46  Rain_luo  阅读(130)  评论(0编辑  收藏  举报