题目链接: 3117 ( Fibonacci Numbers )
位数小于 \(8\) 的可以打表直接输出。
对于位数大于 \(8\) 的,直接用大数求 \(Fibonacci\) 的第 \(n\) 项会 \(TLE\) ,因此需分别求解前 \(4\) 位和后 \(4\) 位。
后 \(4\) 位直接矩阵快速幂取模即可,参见 poj3070 Fibonacci(矩阵快速幂取模) ,或者直接打表后四位找循环节更简单,网上有博主表示第 \(1500\) 项开始出现循环。
至于前四位,需要用到 \(Fibonacci\) 数列的通项公式:
\[\begin{align} Fib_n&=\frac{1}{\sqrt{5}}[(\frac{1+\sqrt5}{2})^n-(\frac{1-\sqrt5}{2})^n] \\ &\approx \frac{1}{\sqrt{5}}(\frac{1+\sqrt5}{2})^n(n\ge0) \\ \epsilon &=\vert\frac{1}{\sqrt{5}}(\frac{1-\sqrt5}{2})^n\vert\le\frac1{\sqrt5}<\frac12 \end{align} \]因此,\(Fibonacci\) 的第 \(n\) 项即为 \(\frac{1}{\sqrt{5}}(\frac{1+\sqrt5}{2})^n\) 四舍五入。
利用这一公式结合对数运算可直接求出第 \(n\) 个 \(Fibonacci\) 数的前四位。
/**
* hdu3117 Fibonacci Numbers
*
*/
#include <iostream>
#include <climits>
#include <vector>
#include <cmath>
#include <iomanip>
using namespace std;
typedef long long LL;
struct Matrix
{
LL mod;
vector<vector<LL> > m;
Matrix(int row, int col, LL mod = LLONG_MAX)
{
this->mod = mod;
m.resize(row, vector<LL>(col));
}
Matrix operator * (const Matrix &t) const
{
int row = m.size(), col = t.m[0].size(), cnt = m[0].size();
Matrix res(row, col, mod);
for (int i = 0; i < row; ++i) {
for (int j = 0; j < col; ++j) {
for (int k = 0; k < cnt; ++k) {
res.m[i][j] += m[i][k]*t.m[k][j]%mod;
res.m[i][j] %= mod;
}
}
}
return res;
}
};
Matrix fastPow(Matrix a, LL b, LL mod = LLONG_MAX)
{
int n = a.m.size();
Matrix t(n, n, mod);
for (int i = 0; i < n; ++i) t.m[i][i] = 1;
while (b) {
if (b&1) t = t*a;
a = a*a;
b >>= 1;
}
return t;
}
const int mod = 10000;
int main()
{
// 位数小于等于8则查表直接输出
vector<long long> f = {0, 1};
for (int i = 2; f[i-1] < 1e8; ++i) {
LL t = f[i-1]+f[i-2];
if (t > 1e8) break;
f.push_back(t);
}
int n;
Matrix A(2, 2, mod);
A.m[0][0] = A.m[0][1] = A.m[1][0] = 1;
while (cin >> n) {
if (n < f.size()) {
cout << f[n] << endl;
continue;
}
double res = n*log10((1+sqrt(5))/2) - log10(sqrt(5));
res = res - (LL) res;
res = pow(10, res);
LL ans = res*1000;
cout << ans << "..."
<< setw(4) << setfill('0') // 后四位可能有前导0
<< fastPow(A, n, mod).m[0][1] << endl;
}
return 0;
}