JZOJ1240. Fibonacci sequence

题目

这里写图片描述

分析

斐波拉契数列是大家所熟悉的,至于怎样求斐波拉契数列的第N项相信大家都知道。

我们用 sum 来表示前缀和
sumn=ni=1fi fi 表示斐波拉契数列的第i项。

显然 sumn=sumn1+fn

我们进一步化简,
尝试将它化简成只与 sum 的式子

sumn=sumn1+fn1+fn2

看起来现在好像没有办法,
但其实可以接着化简。

sumn=2sumn1sumn3

  • 现在我们就知道一个递推式。

但是, x,y 非常大,我们考虑矩阵乘法。

因为只与sum的前三项有关,所以我们就用一个 13 的矩阵×一个 33 的矩阵

一个 13 的矩阵就是 [sumn3,sumn2,sumn1]

而得到的结果也是一个 13 的矩阵,

这个矩阵就是 [sumn2,sumn1,sumn]

根据矩阵乘法的原理,
我们就可以推出中间的转移矩阵。

因为矩阵乘法满足乘法结合律,所以可以使用快速幂。

最终的时间复杂度 O((logx+logy)T) ,是可以通过的。

  • 注意有的地方需要开 long long

code

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define ll long long 
#define mo %10000
using namespace std;

typedef int arr [3][3];

int sta[3]={1,2,4};
int ans[3];
int n,x,y,ans1,ans2,ans3;
arr sum;

void ksm(int x)
{
    arr s,t;
    memset(sum,0,sizeof(sum));
    memset(s,0,sizeof(s));
    s[1][0]=1;
    s[2][1]=1;
    s[0][2]=-1;
    s[2][2]=2;

    if(x)
    {
        sum[1][0]=1;
        sum[2][1]=1;
        sum[0][2]=-1;
        sum[2][2]=2;
        x--;
    }
    while(x)
    {
        if(x%2)
        {
            memcpy(t,sum,sizeof(t));
            memset(sum,0,sizeof(sum));
            for(int i=0;i<=2;i++)
                for(int j=0;j<=2;j++)
                    for(int k=0;k<=2;k++)
                        sum[i][j]=(sum[i][j]+(s[i][k]*t[k][j])mo)mo;
        }

        memcpy(t,s,sizeof(t));
        memset(s,0,sizeof(s));
        for(int i=0;i<=2;i++)
            for(int j=0;j<=2;j++)
                for(int k=0;k<=2;k++)
                    s[i][j]=(s[i][j]+(t[i][k]*t[k][j])mo)mo;

        x=x/2;
    } 
}

int main()
{
    scanf("%d",&n);
    for(int k=1;k<=n;k++)
    {
        scanf("%d%d",&x,&y);
        if(x>4)
        {
            ksm(x-4);
            memset(ans,0,sizeof(ans));
            for(int i=0;i<=2;i++)
                for(int j=0;j<=2;j++)
                    ans[i]=(ans[i]+(sta[j]*sum[j][i])mo)mo;
            ans1=ans[2];
        }
        else
        {
            if(x==1)ans1=0;else ans1=sta[x-2];
        }

        if(y>3)
        {
            ksm(y-3);
            memset(ans,0,sizeof(ans));
            for(int i=0;i<=2;i++)
                for(int j=0;j<=2;j++)
                    ans[i]=(ans[i]+(sta[j]*sum[j][i])mo)mo;
            ans2=ans[2];
        }
        else ans2=sta[y-1];

        ans3=ans2-ans1; 
        while(ans3<0)ans3+=10000;
        ans3=ans3 mo;
        printf("%d\n",ans3);
    }
}

你可能感兴趣的:(题解,斐波拉契数列,矩阵乘法)