对于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;
}
复制代码