题解:Fibonacci 前 n 项和 (矩阵乘法与快速幂)

前言

\(loj\) 上的题,题目链接

我们高级的 \(loj\) 又双叒叕卡死了,所以我用 \(vjudge\)\(loj\) 绑上了。

题面

\(f_x=\begin{cases} 1&x\in\{1,2\}\\ f_{x-1}+f_{x-2}&x \geq 3\\ \end{cases}\)

定义 \(s_x=\sum\limits_{i=1}^nf_i\)

\(s_n\)

方法一

  • 结论:

\[s_n=f_{n+2}-1 \]

  • 证明:

    \(∵f_{n+2}=f_{n+1}+f_n\)

    \(∴f_n=f_{n+2}-f_{n+1}\)

    已知 \(s_n=f_n+f_{n-1}+…+f_1\)

    \(∴s_n=f_{n+2}-f_{n+1}+f_{n-1}+…+f_1\)

    \(∵ f_{n+1}=f_{n}+f_{n-1}\)

    \(∴s_n=f_{n+2}-f_n+f_{n-2}+…+f_1\)

    同理消去 \(f_{n}\)\(f_{n-2}\)

    \(s_n=f_{n+2}-f_{n+1}+f_{n-3}+…+f_3\)

    如此循环,直到:

    \(s_n=f_{n+2}-f_3+f_1\)

    \(s_n=f_{n+2}-2+1\)

    \(s_n=f_{n+2}-1\)

    由此,通过矩阵乘法与快速幂求出 \(f_{n+2}\) ,再 \(-1\) 即可。

点击查看代码
#include<bits/stdc++.h>
#define int long long 
#define endl '\n'
using namespace std;
const int N=10;
template<typename Tp> inline void read(Tp&x)
{
    x=0;register bool z=true;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
}
int n,P,ans[N][N],c[N][N],a[N][N];
void qpow(int b)
{
    for(;b;b>>=1)
    {
        if(b&1)
        {
            for(int i=1;i<=2;i++)   
                for(int j=1;j<=2;j++)
                    for(int k=1;k<=2;k++)
                        (c[i][j]+=(ans[k][j]*a[i][k])%P)%P;
            for(int i=1;i<=2;i++)
                for(int j=1;j<=2;j++)   
                    ans[i][j]=c[i][j],c[i][j]=0;
        }
        for(int i=1;i<=2;i++)
            for(int j=1;j<=2;j++)
                for(int k=1;k<=2;k++)
                    (c[i][j]+=(a[i][k]*a[k][j])%P)%P;
        for(int i=1;i<=2;i++)
            for(int j=1;j<=2;j++)
                a[i][j]=c[i][j],c[i][j]=0;
    }
}
signed main()
{
	#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    read(n),read(P);
    a[1][1]=1;
    a[2][1]=1;
    a[1][2]=1;
    a[2][2]=0;
    for(int i=1;i<=2;i++)
        ans[i][i]=1;
    qpow(n);
    cout<<(ans[1][1]+ans[1][2]-1)%P;
}

方法二

  • 前言:

    其实第一次是用这个方法做的,当时发现一个小结论,但不知道怎么证明的,直接用就 \(A\) 了。

  • 结论:

    \(s_n=s_{n-1}+s_{n-2}+1\)

  • 证明:

    需要在已知 \(s_n=f_{n+2}-1\) 条件下证明。

    \(∵ s_n=f_{n+2}-1\)

    \(~~~~s_{n-1}=f_{n+1}-1\)

    \(~~~~s_{n-2}=f_{n}-1\)

    \(~~~~f_{n+2}=f_{n+1}+f_n\)

    \(∴ s_n=f_{n+2}-1=f_{n+1}+f_{n}-1=s_{n-1}+s_{n-2}+1\)

    于是就可以用和求 \(f_n\) 差不多大方式用矩阵乘法与快速幂求了。

    将矩阵范围扩展到 \(3\times 3\),多出来那一维用来搞这个 \(+1\)

  • 另一种证明方法:

    好的的刚才 \(jjidawang\) 来教了不用 \(s_n=f_{n+2}-1\) 的方法。(2024年03月13日15:35:43)

    \(∵ s_{n-1}=f_1+f_2+…+f_{n-2}+f_{n-1}\)

    \(~~~~s_{n-2}=f_1+f_2+…+f_{n-2}\)

    将其错开一位相加。

    \(f_1+f_2+f_3+…+f_{n-1}\)

    \(~~~~~~~~~+~~~~+~~~~~~~~~~~~~~~+\)

    \(~~~~~~~~~f_1~~~~~f_2~~~~~~~~~~~~~~f_{n-2}\)

    \(∵ f_x=f_{x-1}+f_{x-2}\)

    从而得到 \(s_{n-1}+s_{n-2}=f_1+f_3+f_4+…+f_n\)

    \(∴ s_n=f_1+f_2+f_3+…+f_n\)

    \(~~~~~~~~=s_{n-1}+s_{n-2}+f_2\)

    \(~~~~~~~~=s_{n-1}+s_{n-2}+1\)

点击查看代码
#include<bits/stdc++.h>
#define int long long 
#define endl '\n'
using namespace std;
const int N=10;
template<typename Tp> inline void read(Tp&x)
{
    x=0;register bool z=true;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
}
int n,P,ans[N][N],c[N][N],a[N][N];
void qpow(int b)
{
    for(;b;b>>=1)
    {
        if(b&1)
        {
            for(int i=1;i<=3;i++)   
                for(int j=1;j<=3;j++)
                    for(int k=1;k<=3;k++)
                        (c[i][j]+=(ans[k][j]*a[i][k])%P)%P;
            for(int i=1;i<=3;i++)
                for(int j=1;j<=3;j++)   
                    ans[i][j]=c[i][j],c[i][j]=0;
        }
        for(int i=1;i<=3;i++)
            for(int j=1;j<=3;j++)
                for(int k=1;k<=3;k++)
                    (c[i][j]+=(a[i][k]*a[k][j])%P)%P;
        for(int i=1;i<=3;i++)
            for(int j=1;j<=3;j++)
                a[i][j]=c[i][j],c[i][j]=0;
    }
}
signed main()
{
	#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    read(n),read(P);
    a[1][1]=1,a[1][2]=1,a[1][3]=1;
    a[2][1]=1,a[2][2]=0,a[2][3]=0;
    a[3][1]=0,a[3][2]=0,a[3][3]=1;
    ans[1][1]=1,ans[2][2]=1,ans[3][3]=1;
    qpow(n-2);
    cout<<(2*ans[1][1]+ans[1][2]+ans[1][3])%P;
}

方法三

  • 前言:

    可能这才是官方正解吧。

    因为前面的都是用结论堆的。

  • 解法:

    直接看图:

    image

    很好理解对吧。

    所以和之前一样的:

    image

    这里注意一下 \(s_2=2\) 即可。

点击查看代码
#include<bits/stdc++.h>
#define int long long 
#define endl '\n'
using namespace std;
const int N=10;
template<typename Tp> inline void read(Tp&x)
{
    x=0;register bool z=true;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
}
int n,P,ans[N][N],c[N][N],a[N][N];
void qpow(int b)
{
    for(;b;b>>=1)
    {
        if(b&1)
        {
            for(int i=1;i<=3;i++)   
                for(int j=1;j<=3;j++)
                    for(int k=1;k<=3;k++)
                        (c[i][j]+=(ans[k][j]*a[i][k])%P)%P;
            for(int i=1;i<=3;i++)
                for(int j=1;j<=3;j++)   
                    ans[i][j]=c[i][j],c[i][j]=0;
        }
        for(int i=1;i<=3;i++)
            for(int j=1;j<=3;j++)
                for(int k=1;k<=3;k++)
                    (c[i][j]+=(a[i][k]*a[k][j])%P)%P;
        for(int i=1;i<=3;i++)
            for(int j=1;j<=3;j++)
                a[i][j]=c[i][j],c[i][j]=0;
    }
}
signed main()
{
	#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    read(n),read(P);
    a[1][1]=1,a[1][2]=1,a[1][3]=1;
    a[2][1]=0,a[2][2]=1,a[2][3]=1;
    a[3][1]=0,a[3][2]=1,a[3][3]=0;
    for(int i=1;i<=3;i++)
        ans[i][i]=1;
    qpow(n-2);
    cout<<(2*ans[1][1]+ans[1][2]+ans[1][3])%P;
}
posted @ 2024-03-13 17:23  卡布叻_周深  阅读(40)  评论(3编辑  收藏  举报