對於Febonacci數列,求第n項和第m項的最大公約數是多少?(n,m<=1e9>)。對於最後的結果只要輸出最後的8位數字就能夠了。ios
首先注意到的是數據量巨大(1e9),數組開不下;即便使用Febonacci遞推公式也會超時。不過好在Febonacci數列是有通項公式的。由於,解特徵方程
獲得解
,再代入
,就能夠解的最終的通項公式是
。數組
求兩個數的最大公約數能夠用 展轉相除法ui
typedef long long LL;
LL gcd(LL a, LL b) {
return (b ? gcd(b, a % b) : a);
}
複製代碼
不過這裏的數字會很是巨大,而且也不是一個整數,用快速冪的時候對double作
%
運算彷佛會報錯……spa
正解用到了 矩陣加速 和 一個Febonacci的結論。 矩陣加速並非必須的,可是能減小很多計算量。先看看矩陣加速的巧妙之處:code
若是已知:,那麼要求
,只須要乘一個矩陣:ip
其實這裏已經能看出用矩陣的方式表達Febonacci數列的遞推關係。若是初始化矩陣爲 ,那麼通項公式爲:ci
這樣作就能使用 矩陣快速冪 減少複雜度到 O(logn)
算得 F[n]
。get
再看一下這道題的 關鍵之處string
先看結論:it
這個結論彷佛很好記,可是不是很直接,Febonacci數列的第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;
}
複製代碼