斐波那契公約數

題目描述

對於Febonacci數列,求第n項和第m項的最大公約數是多少?(n,m<=1e9>)。對於最後的結果只要輸出最後的8位數字就能夠了。ios

思路

首先注意到的是數據量巨大(1e9),數組開不下;即便使用Febonacci遞推公式也會超時。不過好在Febonacci數列是有通項公式的。由於F[n] = F[n-2] + F[n-1],解特徵方程X^{2}=X+1獲得解X=(1 \pm \sqrt{5})/2,再代入F[n]=aX_{1}^{n} + bX_{2}^{n},就能夠解的最終的通項公式是F[n] = \frac{1}{\sqrt{5}} × [(\frac{1+\sqrt{5}}{2})^n-(\frac{1-\sqrt{5}}{2})^n]數組

求兩個數的最大公約數能夠用 展轉相除法ui

typedef long long LL;
LL gcd(LL a, LL b) {
    return (b ? gcd(b, a % b) : a); 
}
複製代碼

不過這裏的數字會很是巨大,而且\sqrt{5}也不是一個整數,用快速冪的時候對double作%運算彷佛會報錯……spa

題解

正解用到了 矩陣加速一個Febonacci的結論。 矩陣加速並非必須的,可是能減小很多計算量。先看看矩陣加速的巧妙之處:code

矩陣加速

若是已知:(F[2], F[1])^T,那麼要求(F[3], F[2])^T,只須要乘一個矩陣:ip

\left [
    \begin{matrix}
        1 & 1 \\
        1 & 0 \\
    \end{matrix}
\right ]

其實這裏已經能看出用矩陣的方式表達Febonacci數列的遞推關係。若是初始化矩陣爲 (F[2], F[1])^T,那麼通項公式爲:ci

(F[n], F[n-1])^T = \left [
    \begin{matrix}
        1 & 1 \\
        1 & 0 \\
    \end{matrix}
\right ]^{n-2} (F[2], F[1])^T   (n \ge 2)

這樣作就能使用 矩陣快速冪 減少複雜度到 O(logn) 算得 F[n]get

再看一下這道題的 關鍵之處string

一個Febonacci的結論

先看結論:it

gcd(F[n], F[m]) = F[gcd(n, m)]

這個結論彷佛很好記,可是不是很直接,Febonacci數列的第n項和第m項的最大公約數竟然正好等於第gcd(n,m)項的值?

詳細的推導過程是這樣的:

  1. 若是F[n]=a, F[n+1]=b
  2. 那麼F[n+2]=a+b, F[n+3] = a+2b, ... ,F[m] = F[m−n−1]a + F[m−n]b
  3. 因此有F[m] = F[m−n−1] ∗ F[n] + F[m−n] ∗ F[n+1]
  4. 這樣,gcd(F[n], F[m]) = gcd(F[n], F[m−n−1] ∗ F[n] + F[m−n] ∗ F[n+1])
  5. 其中F[m−n−1] ∗ F[n]F[n]的倍數,所以上式轉爲gcd(F[n], F[m]) = gcd(F[n], F[m−n] ∗ F[n+1])
  6. 能夠經過遞推證實:gcd(F[n], F[n+1]) = 1
  7. 所以gcd(F[n], F[m]) = gcd(F[n], F[m−n]),即gcd(F[n], F[m]) = gcd(F[n], F[m%n])

這樣求解的目標就化簡爲求第gcd(n,m)項了。

代碼

#include <cstring>
#include <iostream>
#define MOD 100000000

using namespace std;
typedef long long LL;

LL gcd(LL a, LL b) {
    return (b ? gcd(b, a%b) : a);
}

struct Mat {
    LL mat[2][2];
    int h, w;
    Mat(int _h=2, int _w=2) {
        h = _h, w = _w;
        memset(mat, 0, sizeof(mat));
    }
};

Mat Multiply(const Mat &a, const Mat &b) {
    Mat ret(a.h, b.w);
    for (int i = 0; i < a.h; ++i)
    for (int j = 0; j < b.w; ++j)
    for (int k = 0; k < a.w; ++k) {
        LL tmp = a.mat[i][k] * b.mat[k][j] % MOD;
        ret.mat[i][j] = (ret.mat[i][j] + tmp) % MOD;
    }
    return ret;
}

int main() {
    LL n, m;
    cin >> n >> m;
    LL gcd_nm = gcd(n, m);
    if (gcd_nm <= 2) cout << 1 << endl;
    else {
        Mat raw(2, 1);
        raw.mat[0][0] = raw.mat[1][0] = 1;
        Mat f(2, 2);
        f.mat[0][0] = f.mat[0][1] = f.mat[1][0] = 1;
        gcd_nm -= 2;
        while (gcd_nm) {
            if (gcd_nm & 1)
                raw = Multiply(f, raw);
            f = Multiply(f, f);
            gcd_nm >>= 1;
        }
        cout << raw.mat[0][0] << endl;
    }
    return 0;
}
複製代碼
相關文章
相關標籤/搜索