SPOJ - FIBOSUM Fibonacci Sum(递推公式/矩阵快速幂)

传送门


解法一

找规律不难发现斐波那契数列的前缀和和斐波那契数列有直接的联系,也就是 s ( n ) = f ( n + 2 ) − 1 s(n)=f(n+2)-1 s(n)=f(n+2)1,这个可以通过数学归纳法证明或者通过斐波那契数列的公式 f ( n ) = 1 5 [ ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ] f(n)=\frac{1}{\sqrt{5}}[(\frac{1+\sqrt{5}}{2})^n-(\frac{1-\sqrt{5}}{2})^n] f(n)=5 1[(21+5 )n(215 )n],然后考虑等比数列求和公式

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int Mod=1e9+7;

struct Matrix{
    ll matrix[105][105];
};

int n;

Matrix mul(Matrix a,Matrix b){
    Matrix ans;
    memset(ans.matrix,0,sizeof ans.matrix);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    for(int k=1;k<=n;k++){
        ans.matrix[i][j]+=a.matrix[i][k]*b.matrix[k][j]%Mod;
        ans.matrix[i][j]%=Mod;
    }
    return ans;
}

Matrix qkp(Matrix mx,ll x){
    Matrix ans;
    memset(ans.matrix,0,sizeof ans.matrix);
    for(int i=1;i<=n;i++) ans.matrix[i][i]=1;
    while(x){
        if(x&1) ans=mul(ans,mx);
        mx=mul(mx,mx);
        x>>=1;
    }
    return ans;
}


int main(){
    ios_base::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int t,l,r;
    Matrix mx;
    n=2;
    mx.matrix[1][1]=1,mx.matrix[1][2]=1;
    mx.matrix[2][1]=1,mx.matrix[2][2]=0;
    cin>>t;
    while(t--){
        cin>>l>>r;
        r+=2,l+=2;
        Matrix res1=qkp(mx,r),res2=qkp(mx,l-1);
        cout<<(res1.matrix[2][1]-res2.matrix[2][1]+Mod)%Mod<<endl;
    }
    return 0;
}
解法二

我们可以得到关于斐波那契前缀和的这样一个递推式:

s ( n ) = s ( n − 1 ) + f ( n ) = s ( n − 1 ) + f ( n − 1 ) + f ( n − 2 ) = s ( n − 1 ) + s ( n − 1 ) − s ( n − 3 ) = 2 ∗ s ( n − 2 ) − s ( n − 3 ) s(n)=s(n-1)+f(n)=s(n-1)+f(n-1)+f(n-2)=s(n-1)+s(n-1)-s(n-3)=2*s(n-2)-s(n-3) s(n)=s(n1)+f(n)=s(n1)+f(n1)+f(n2)=s(n1)+s(n1)s(n3)=2s(n2)s(n3)

即: s ( n ) = 2 ∗ s ( n − 1 ) + 0 ∗ s ( n − 2 ) − 1 ∗ s ( n − 3 ) s(n)=2*s(n-1)+0*s(n-2)-1*s(n-3) s(n)=2s(n1)+0s(n2)1s(n3)

然后又因为:

s ( n − 1 ) = 1 ∗ s ( n − 1 ) + 0 ∗ s ( n − 2 ) + 0 ∗ s ( n − 3 ) s(n-1)=1*s(n-1)+0*s(n-2)+0*s(n-3) s(n1)=1s(n1)+0s(n2)+0s(n3)

s ( n − 2 ) = 0 ∗ s ( n − 1 ) + 1 ∗ s ( n − 2 ) + 0 ∗ s ( n − 3 ) s(n-2)=0*s(n-1)+1*s(n-2)+0*s(n-3) s(n2)=0s(n1)+1s(n2)+0s(n3)

通过一些推导可以得到如下一个矩阵,推导方法见我的博客我们可以得到如下一个矩阵:

[ s ( n ) s ( n + 1 ) s ( n + 2 ) s ( n − 1 ) s ( n ) s ( n + 1 ) s ( n − 2 ) s ( n − 1 ) s ( n ) ] = [ 2 0 − 1 1 0 0 0 1 0 ] n [ s ( 0 ) s ( 1 ) s ( 2 ) s ( 1 ) s ( 0 ) s ( 1 ) s ( − 2 ) s ( − 1 ) s ( 0 ) ] { \left[ \begin{array}{ccc} s(n) & s(n+1) & s(n+2)\\ s(n-1) & s(n) & s(n+1)\\ s(n-2) & s(n-1) & s(n) \end{array} \right ]}={ \left[ \begin{array}{ccc} 2 & 0 & -1\\ 1 & 0 & 0\\ 0 & 1 & 0 \end{array} \right ]}^n{ \left[ \begin{array}{ccc} s(0) & s(1) & s(2)\\ s(1) & s(0) & s(1)\\ s(-2) & s(-1) & s(0) \end{array} \right ]} s(n)s(n1)s(n2)s(n+1)s(n)s(n1)s(n+2)s(n+1)s(n)=210001100ns(0)s(1)s(2)s(1)s(0)s(1)s(2)s(1)s(0)

然后可以通过 s ( 2 ) = 2 ∗ s ( 1 ) − s ( − 1 ) , s ( 1 ) = 2 ∗ s ( 0 ) − s ( − 2 ) s(2)=2*s(1)-s(-1),s(1)=2*s(0)-s(-2) s(2)=2s(1)s(1),s(1)=2s(0)s(2)求出 s ( − 1 ) 和 s ( − 2 ) s(-1)和s(-2) s(1)s(2),虽然这样没有科学依据而正解是通过矩阵的逆,然后通过几个一元一次方程求解,但是我发现其实和这样解出的结果是一样的,那就干脆这样求好了,之前写的几道这样求也是对的

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int Mod=1e9+7;

struct Matrix{
    ll matrix[105][105];
};

int n;

Matrix mul(Matrix a,Matrix b){
    Matrix ans;
    memset(ans.matrix,0,sizeof ans.matrix);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    for(int k=1;k<=n;k++){
        ans.matrix[i][j]+=a.matrix[i][k]*b.matrix[k][j]%Mod;
        if(ans.matrix[i][j]<0) ans.matrix[i][j]=Mod+ans.matrix[i][j];  //中间结果可能为负数那么需要加上Mod
        else ans.matrix[i][j]%=Mod;
    }
    return ans;
}

Matrix qkp(Matrix mx,ll x){
    Matrix ans;
    memset(ans.matrix,0,sizeof ans.matrix);
    if(x==-1) return ans;
    for(int i=1;i<=n;i++) ans.matrix[i][i]=1;
    while(x){
        if(x&1) ans=mul(ans,mx);
        mx=mul(mx,mx);
        x>>=1;
    }
    return ans;
}


int main(){
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int t,l,r,q;
    Matrix mx,dw;
    n=3;
    mx.matrix[1][1]=2,mx.matrix[1][2]=0,mx.matrix[1][3]=-1;
    mx.matrix[2][1]=1,mx.matrix[2][2]=0,mx.matrix[2][3]=0;
    mx.matrix[3][1]=0,mx.matrix[3][2]=1,mx.matrix[3][3]=0;

    dw.matrix[1][1]=0,dw.matrix[1][2]=1,dw.matrix[1][3]=2;
    dw.matrix[2][1]=0,dw.matrix[2][2]=0,dw.matrix[2][3]=1;
    dw.matrix[3][1]=-1,dw.matrix[3][2]=0,dw.matrix[3][3]=0;

    cin>>t;
    while(t--){
        cin>>l>>r;
        Matrix res1=qkp(mx,r),res2=qkp(mx,l-1);
        res1=mul(res1,dw),res2=mul(res2,dw);
        cout<<(res1.matrix[1][1]-res2.matrix[1][1]+Mod)%Mod<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值