zoukankan      html  css  js  c++  java
  • 「学习小结」斐波那契数列

    一、定义

    斐波那契数列是满足如下性质的一个数列:

    (F_n = left{egin{aligned} 1 space (n le 2) \ F_{n-1}+F_{n-2} space (nge 3) end{aligned} ight.)

    有通项公式:

    (displaystyle F_n=frac{1}{sqrt{5}}left[{left(frac{1+sqrt{5}}{2} ight)}^n-{left(frac{1-sqrt{5}}{2} ight)}^n ight])

    快速求斐波那契数列的第 (n) 项:矩阵快速幂 优化。

    二、性质

    1. 求和性质

    • 直接求和:(F_1+F_2+cdots+F_n=F_{n+2}-1)

      • 证明:(sum_{i=1}^n F_i=sum_{i=1}^n {F_{i+2}-F_{i+1}}=F_{n+2}-F_2=F_{n+2}-1)
    • 奇数项求和:(F_1+F_3+F_5+F_7+cdots+F_{2n-1}=F_{2n})

      • 证明:(F_1+F_3+F_5+F_7+cdots+F_{2n-1})(=F_1+(F_1+F_2)+(F_3+F_4)+(F_5+F_6)+cdots+(F_{2n-3}+F_{2n-2}))(=F_1+sum_{i=1}^{2n-2}F_i=F_1+(F_{2n}-1)=F_{2n})
    • 偶数项求和:(F_2+F_4+F_6+F_8+cdots+F_{2n}=F_{2n+1}-1)

      • 证明:(F_2+F_4+F_6+F_8+cdots+F_{2n})(=F_2+(F_2+F_3)+(F_4+F_5)+(F_6+F_7)+cdots+(F_{2n-2}+F_{2n-1}))(=F_2+sum_{i=1}^{2n-1}F_i-F_1=F_{2n+1}-1)
    • 平方求和:(F_1^2+F_2^2+F_3^2+F_4^2+cdots+F_n^2=F_ncdot F_{n+1})

      • 证明:归纳法。显然对于 (n=1) 结论成立。假设有 (sum_{i=1}^{n-1} F_i^2=F_{n-1}F_n) 成立,那么 (sum_{i=1}^n F_i^2=F_{n-1}F_n+F_n^2=F_nF_{n+1})。(也可以尝试 画图证明。以 (F_i) 作为正方形的边长。)

    2. 一些等式

    • 卡西尼恒等式(Cassini's identity):(F_{n-1}F_{n+1}-F_n^2=(-1)^n)。

    • 附加性质:(F_{n+m}=F_mF_{n+1}+F_{m-1}F_n)。注意到将 (m=n) 代入原式可以得到另一性质:(F_{2n}=F_n(F_{n-1}+F_{n+1}))

      • 证明:(n) 进行归纳法。当 (n=1) 时,(F_{m+1}=F_mF_2+F_{m-1}F_1=F_m+F_{m-1}),结论成立。

      • 假设有 (F_{(n-1)+m}=F_mF_{(n-1)+1}+F_{m-1}F_{n-1}) 成立。则 (F_{n+m}=F_{(n-1)+(m+1)}=F_{m+1}F_{(n-1)+1}+F_{(m+1)-1}F_{n-1})(=(F_{m-1}+F_m)F_n+F_mF_{n-1}=F_{m-1}F_n+F_m(F_n+F_{n-1})=F_{m-1}F_n+F_mF_{n+1})

    3. 最大公约数相关

    • 相邻项性质:(gcd(F_n,F_{n+1})=1)

      • 证明:归纳法。显然对于 (n=1) 结论成立。假设有 (gcd(F_{n-1},F_n)=1) 成立,那么:(gcd(F_n,F_{n+1})=gcd(F_n,F{n+1}-F_n)=gcd(F_n,F_{n-1})=1)
    • 最大公约数性质:(gcd(F_n,F_m)=F_{gcd(n,m)})

      • 证明:不妨设 (n<m),根据附加性质,(gcd(F_n,F_m)=gcd(F_n,F_{n+(m-n)})=gcd(F_n,F_{m-n}F_{n+1}+F_{m-n-1}F_n))(=gcd(F_n,F_{m-n}F_{n+1}))

      • 由相邻项性质可知 (gcd(F_n,F_{n+1})=1),所以 (gcd(F_n,F_m)=gcd(F_n,F_{m-n}F_{n+1})=gcd(F_n,F_{m-n}))

      • 递归下去就是辗转相减法。最终会得到的形式应当就是 (gcd(F_n,F_m)=F_{gcd(n,m)})

    • 其他性质:(nmid m⇔F_nmid F_m)

      • 证明:(nmid m) 时,(gcd(F_n,F_m)=F_{gcd(n,m)}=F_n),所以 (F_nmid F_m)

    4. 项与项之间的关系

    • 平方与前后项:(F_n^2-F_{n-1}cdot F_{n+1}=(-1)^{n-1})

    • 隔项关系:(F_{2n-2m-2}(F_{2n}+F_{2n+2})=F_{2m+2}+F_{4n-2m})(n>mgeq -1,ngeq 1))。

    • 两倍项关系:(dfrac{F_{2n}}{F_n}=F_{n-1}+F_{n+1})

    三、模意义下周期性

    在模 (p) 意义下,斐波那契的第 (n+1)(F_{n+1}) 仅取决于 ((F_{n−1}mod p,F_nmod p))。这个二元组有 (p^2) 种取值。

    (p) 的剩余系大小为 (p),意味着在前 (p^2+1) 个二元组中必有两个相同的数对。于是这两个二元组往后生成相同的斐波那契数列,那么它们就是有周期性的。

    所以斐波那契数列在模意义下一定会产生循环节,并且一定是纯循环的。

    事实上,有一个远比它强的结论:形如 (n=2 imes 5^k) 的循环节均为 (6n),并且可以证明斐波那契数列模 (n) 意义下的最小循环节不超过 (6n)。详见 此处

    四、例题

    1. SPOJ FIBOSUM - Fibonacci Sum

    题目大意:(sum_{i=n}^m F_i)(10^9+7) 取模的值。其中 (F_i) 表示斐波那契数列第 (i) 项的值。(Tleq 1000,0leq nleq mleq 10^9)

    Solution:

    根据性质 (F_1+F_2+cdots+F_n=F_{n+2}-1) 可得:

    (sum_{i=n}^m F_i=sum_{i=1}^m F_i-sum_{i=1}^{n-1} F_i=(F_{m+2}-1)-(F_{n+1}-1)=F_{m+2}-F_{n+1})

    矩阵快速幂优化即可。

    #include<bits/stdc++.h>
    #define int long long 
    using namespace std;
    const int N=2,mod=1e9+7;
    int t,n,m,f[N][N];
    void mul(int x[N][N],int y[N][N]){
        int c[N][N];
        memset(c,0,sizeof(c));
        for(int i=0;i<N;i++)
            for(int j=0;j<N;j++)
                for(int k=0;k<N;k++)
                    c[i][j]=(c[i][j]+x[i][k]*y[k][j])%mod;
        memcpy(x,c,sizeof(c));
    }
    int query(int n){    //求斐波那契数列第 n 项的值 
        int a[N][N]={{0,1},{1,1}};
        memset(f,0,sizeof(f)),n++;
        for(int i=0;i<N;i++) f[i][i]=1;
        for(;n;n>>=1,mul(a,a))
            if(n&1) mul(f,a);
        return f[0][0];
    } 
    signed main(){
        scanf("%lld",&t);
        while(t--){
            scanf("%lld%lld",&n,&m);
            printf("%lld
    ",(query(m+2)-query(n+1)+mod)%mod);
        }
        return 0;
    }

    2. CF226C Anniversary

    题目大意:给定 (m,l,r,k)。在 ([l,r]) 中选出 (k) 个数 (a_1,a_2,cdots,a_k),使得 (gcd(F_{a_1},F_{a_2},cdots,F_{a_k})) 尽可能大。求对 (m) 取模后的结果。

    Solution:

    根据性质 (gcd(F_n,F_m)=F_{gcd(n,m)}) 可得:(gcd(F_{a_1},F_{a_2},cdots,F_{a_k})=F_{gcd(a_1,a_2,cdots,a_k)})

    由于 (F_n=F_{n-1}+F_{n-2}>F_{n-1}),所以 (gcd(a_1,a_2,cdots,a_k)) 的值越大越好。

    问题转化为在 ([l,r]) 中选 (k) 个数,使得这 (k) 个数的 (gcd) 最大。

    在一个区间 ([l,r]) 中,含有因子 (x) 的数的数量为 (frac{r}{x}-frac{l-1}{x})

    (calc(x)=frac{r}{x}-frac{l-1}{x})。枚举 (1leq ileq sqrt{r}),判断 (calc(i))(calc(frac{r}{i})) 是否大于等于 (k)。记录最大的满足 (calc(x)geq k)(x)

     (x) 即为最大的 (gcd(a_1,a_2,cdots,a_k)) 的值。答案即为 (F_x)

    #include<bits/stdc++.h>
    #define int long long 
    using namespace std;
    const int N=2;
    int l,r,mod,k,f[N][N],res;
    int calc(int x){    //求区间 [l,r] 中,含有因子 x 的数的数量 
        return r/x-(l-1)/x;
    }
    void mul(int x[N][N],int y[N][N]){
        int c[N][N];
        memset(c,0,sizeof(c));
        for(int i=0;i<N;i++)
            for(int j=0;j<N;j++)
                for(int k=0;k<N;k++)
                    c[i][j]=(c[i][j]+x[i][k]*y[k][j])%mod;
        memcpy(x,c,sizeof(c));
    }
    int query(int n){    //求斐波那契数列第 n 项的值 
        int a[N][N]={{0,1},{1,1}};
        memset(f,0,sizeof(f)),n++;
        for(int i=0;i<N;i++) f[i][i]=1;
        for(;n;n>>=1,mul(a,a))
            if(n&1) mul(f,a);
        return f[0][0];
    } 
    signed main(){
        scanf("%lld%lld%lld%lld",&mod,&l,&r,&k);
        for(int i=1;i<=sqrt(r);i++){
            if(calc(i)>=k) res=max(res,i);
            if(calc(r/i)>=k) res=max(res,r/i);    //判断 calc(i) 和 calc(r/i) 是否 >=k,并记录下最大的 i 或 r/i 
        }
        printf("%lld
    ",query(res));
        return 0;
    }

    3. Luogu P3424 [POI2005]SUM-Fibonacci Sums

    题目大意:斐波那契数列:(F_0=1,F_1=1),对于 (ngeq 2),有 (F_n=F_{n-1}+F_{n-2})

    我们可以用若干个斐波那契数列中的数的和的形式表示一个数。为了保证表示的唯一性,我们用 (b_1,b_2,cdots,b_n) 表示数 (b_1cdot F_1+b_2cdot F_2+cdots +b_ncdot F_n)(b_iin {0,1}),注意此题中没有用 (F_0)。并且有如下规定:

    • (n>1),则 (b_n=1),即表示的数没有多余的零。

    • (b_i=1),则 (b_{i+1}=0)(1leq i<n)),即表示中不存在两个(或以上)连续的 (1)

    已知 (x,y) 的斐波那契表示,求 (x+y) 的斐波那契表示。

    Solution:

    首先把两个数列对应位置上的数分别加起来,再进行调整。令 (c_i=a_i+b_i)

    若存在 (c_i>1)

    • (i=1),由于 (F_2=F_0+F_1=2F_1),则令 (c_1←c_1-2)(c_2←c_2+1)

    • 否则:由于 (2F_n=F_{n-1}+F_n+F_{n-2}=F_{n+1}+F_{n-2}),则可以令 (c_i←c_i-2)(c_{i+1}←c_{i+1}+1)(c_{i-2}←c_{i-2}+1)

    若出现 (c_i=1)(c_{i+1}=1),由于 (F_n=F_{n-1}+F_{n-2}),则可以令 (c_i←c_i-1)(c_{i+1}←c_{i+1}-1)(c_{i+2}←c_{i+2}+1)

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int N=1e6+5;
    int n,m,up,a[N],b[N],c[N];
    void solve(int x){    //处理 c[x]>1 的情况 
        if(c[x]<=1) return ;
        c[x]-=2;
        if(x==1) c[2]++;
        else if(x==2) c[1]++,c[3]++;
        else c[x+1]++,c[x-2]++;
    }
    void work(int x){    //处理连续两个数都为 1 的情况 
        while(c[x]>0&&c[x+1]>0) c[x]--,c[x+1]--,c[x+2]++,x+=2;
    } 
    signed main(){
        scanf("%lld",&n);
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        scanf("%lld",&m),up=max(n,m);
        for(int i=1;i<=m;i++)
            scanf("%lld",&b[i]);
        for(int i=1;i<=up;i++)    //先直接加起来 
            c[i]=a[i]+b[i];
        for(int i=up;i>=1;i--)
            solve(i),work(i),solve(i+1),work(i+1),solve(i+2),work(i+2);     //最多影响到后面两位,i+1 和 i+2 也要判 
        if(c[up+1]) up++; if(c[up+2]) up+=2;    //进位。最多影响到后面两位
        printf("%lld ",up);
        for(int i=1;i<=up;i++)
            printf("%lld%c",c[i],i==up?'
    ':' ');
        return 0;
    } 

    五、习题

    • Luogu P3539 [POI2012]ROZ-Fibonacci Representation
    • SPOJ MAIN74 - Euclids algorithm revisited
    • CF633D Fibonacci-ish
    • UVA10236 The Fibonacci Primes
  • 相关阅读:
    tp5.前端模板注释
    站在历史天空下的幻想——高考感怀七十二韵
    公交查询
    模糊查询
    ibox 的使用
    tabs 导航 及内容切换
    c#ADO.NET 执行带参数及有返回数据
    Sql Server中变的定义以及赋值的应用
    EF的3种开发模式
    php7.1.30 下微信公众号问题
  • 原文地址:https://www.cnblogs.com/maoyiting/p/13969446.html
Copyright © 2011-2022 走看看