斐波拉契

在OI简单数论中 斐波拉契是常常出现的东西

|是什么

斐波那契数列,又称黄金分割数列、因数学家列昂纳多·斐波那契以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递归的方法定义:F(0)=1,F(1)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*)在现代物理、准晶体结构、化学等领域,斐波纳契数列都有直接的应用.

(百度百科)

|模板

1,递归

inline int Fib(int n)
{
    if (n == 1 || n == 2)
    {
        return 1;
    }
    return Fib(n - 1) + Fib(n - 2);
}

复杂度分析

由于对于每一个1都是最后一层递归返回上来的故会递归F(n)次 , 由于斐波拉契数列是随着指数上升的 故复杂度约为O(2^n)

2,循环

inline int Fib(int n)
{
    F[1] = F[2] = 1;
    for(int i = 3 ; i <= n ; ++ i)
        F[i] = F[i - 1] + F[i - 2];
    return F[n];
}

复杂度分析 :O(n)

3,矩阵乘法优化

讲数列放在矩阵乘法中会发现

[F[n1]F[n2]][1110]=[F[n]F[n1]]

即如果求F[n]

[F[n]F[n1]]=[F[n1]F[n2]][1110]=[F[n2]F[n3]][1110]2=....=[F[1]F[2]][1110]n1

[F[1]F[2]]

拓展到2 * 2即
[1001]
#include <iostream>
#include <cstdio>
#include <cstring>
#define inc(i) (++ i)
#define int long long
using namespace std;

const int Mod = 1000000000 + 7;
struct JX
{
    int a[3][3];
    friend JX operator * (JX a , JX b)
    {
        JX c;
        memset(c.a , 0 , sizeof(c.a));
        for(int i = 1 ; i <= 2 ; inc(i))
            for(int j = 1 ; j <= 2 ; inc(j))
                for(int k = 1 ; k <= 2 ; inc(k))
                    c.a[i][j] = (c.a[i][j] + a.a[i][k] * b.a[k][j]) % Mod;
        return c;
    }
}Ans , A;
int n;

inline void KSM()
{
    int p = n - 2;
    while(p)
    {
        if(p & 1) Ans = Ans * A;
        p >>= 1 , A = A * A;
    }
}

signed main()
{
    scanf("%ld" , &n);
    if(n <= 2)  putchar(49);
    else
    {
        Ans.a[1][1] = A.a[1][1] = 1;//直接跳到n = 2时的矩阵
        Ans.a[1][2] = A.a[1][2] = 1;
        Ans.a[2][1] = A.a[2][1] = 1;
        KSM();
        printf("%lld" , Ans.a[1][1]);
    }
    return 0;
}

4, 算不上什么优化的优化(斐波拉契数列膜p的循环节)

转自Fib数模n的循环节(代码原创)

我们知道Fibonacci数列,现在我们来求一个Fib数模n的循环节的长度。

对于一个正整数n,我们求Fib数模n的循环节的长度的方法如下:

(1)把n素因子分解,即
(2)分别计算Fib数模每个的循环节长度,假设长度分别是
(3)那么Fib模n的循环节长度

从上面三个步骤看来,貌似最困难的是第二步,那么我们如何求Fib模的循环节长度呢?

这里有一个优美的定理:

Fib数模的最小循环节长度等于,其中表示Fib数模素数的最小循环节长度。可以看出我们现在最重要的就是求

对于求我们利用如下定理:

如果5是模的二次剩余,那么循环节的的长度是的因子,否则,循环节的长度是的因子。

顺便说一句,对于小于等于5的素数,我们直接特殊判断,loop(2)=3,loop(3)=8,loop(5)=20。

那么我们可以先求出所有的因子,然后用矩阵快速幂来一个一个判断,这样时间复杂度不会很大。

模板代码:(巨丑 见谅)

#include <iostream>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <cmath>
#include <algorithm>
#define int long long
#define inc(i) (++ i)
#define dec(i) (-- i)
using namespace std;

const int N = 100000 + 7;
int n , Prime[N + 7] , tot , Pri[N] , Cnt[N] , cnt , TOT;
int Factor[N] , P;
bool No_Prime[N + 7];
struct JX
{
    int a[4][4];
    friend JX operator * (JX a , JX b)
    {
        JX c;
        memset(c.a , 0 , sizeof(c.a));
        for(int i = 1 ; i <= 2 ; inc(i))
            for(int j = 1 ; j <= 2 ; inc(j))
                for(int k = 1 ; k <= 2 ; inc(k))
                    c.a[i][j] = (c.a[i][j] + a.a[i][k] * b.a[k][j] % P) % P;
        return c;
    }
}A , X;

inline JX KSM_2(JX a , int q , int P)  
{//矩阵优化斐波拉契数列
    JX Ans = X , x = a;
    while(q)
    {  
        if(q & 1) Ans = Ans * x;
        q >>= 1 , x = x * x; 
    }
    return Ans;
}

inline void Get_Prime()
{//得出质数便于之后分解质因数
    No_Prime[1] = 1;
    for(int i = 2 ; i <= N ; inc(i))
    {
        if(!No_Prime[i]) Prime[inc(tot)] = i;
        for(int j = 1 ; j <= tot && i * Prime[j] <= N ; inc(j))
        {
            No_Prime[i * Prime[j]] = 1;
            if(i % Prime[j] == 0) break;
        }
    }
}

inline void Resolve(int n , int Pri[] , int Cnt[])
{//分解质因数 , pri存质因子 , cnt存次数
    cnt = 1;
    int t = sqrt(n) , TOT;
    for(int i = 1 ; Prime[i] <= t ; inc(i))
        if(n % Prime[i] == 0)
        {
            TOT = 0;
            Pri[cnt] = Prime[i];
            while(n % Prime[i] == 0)
            {
                n /= Prime[i];
                inc(TOT);
            }
            Cnt[cnt] = TOT;
            inc(cnt);
        }
    if(n ^ 1)
    {
        Pri[cnt] = n , Cnt[cnt] = 1;
        inc(cnt);
    }
    dec(cnt);
}


inline int KSM_1(int x , int q , int P)
{//普通的快速幂
    int Ans = 1;
    x %= P;
    while(q)  
    {
        if(q & 1) Ans = Ans * x % P;
        q >>= 1 , x = x * x % P;
    }
    return Ans;
}

inline void Work(int n)  
{//求n的因数
    TOT = 0;
    int t = sqrt(n);
    for(int i = 1 ; i <= t ; inc(i))  
    {
        if(n % i == 0)  
        {
            if(i * i == n) Factor[inc(TOT)] = i;
            else  
            {
                Factor[inc(TOT)] = i;
                Factor[inc(TOT)] = n / i;
            }
        }
    }
}

inline int Gcd(int a , int b)
{
    return a % b == 0 ? b : Gcd(b , a % b);
}

inline int Find_Loop(int n)
{
    Resolve(n , Pri , Cnt);
    int Ans = 1 , loop;
    for(int i = 1 ; i <= cnt ; inc(i))
    {
        loop = 1;
        P = Pri[i];
        switch(P)
        {
            case 2: loop = 3; break;
            case 3: loop = 8; break;
            case 5: loop = 20; break;
            default :
            {
                if(KSM_1(5 , (P - 1) >> 1 , P) == 1)  
                    Work(P - 1);
                else
                    Work(2 * (P + 1));
                sort(Factor , Factor + TOT + 1);
                for(int j = 1 ; j <= TOT ; inc(j))  
                {
                    JX B = KSM_2(A , Factor[j] - 1 , P);
                    int x = (B.a[1][1] + B.a[1][2]) % P;
                    int y = (B.a[2][1] + B.a[2][2]) % P;
                    if(x == 1 && y == 0)  
                    {
                        loop = Factor[j];
                        break;
                    }
                }
            }break;
        }
        loop *= KSM_1(P , Cnt[i] - 1 , 9223372036854775807);
        if(loop < Ans) swap(loop , Ans);
        Ans = Ans / Gcd(loop , Ans) * loop;
    }
    return Ans;
}

inline int Mod(char* a , int b)//高精度a除以低精度b  
{
    int d = 0 , l = strlen(a);
    for(int i = 0 ; i < l ; inc(i))  d = (d * 10 + (a[i] - '0')) % b;//求出余数  
    return d;
}

char pp[30000007];
signed main()
{
    A.a[1][1] = A.a[1][2] = A.a[2][1] = 1;
    X.a[1][1] = X.a[2][2] = 1;
    Get_Prime();
    scanf("%s%lld" , pp , &n);
    int loop = Find_Loop(n);
    int Ans = Mod(pp , loop);
    if(!Ans) Ans = loop;
    if(Ans <= 0) putchar(48);
    else
        if(Ans <= 2) printf("%lld" , 1ll % n);
        else
        {
            A.a[1][1] = A.a[1][2] = A.a[2][1] = 1;
            X.a[1][1] = X.a[2][2] = 1;
            P = n;
            X = KSM_2(A , Ans - 1 , n);
            printf("%lld" , X.a[1][1]);
        }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章