题目链接
题目大意:输出 Fibonacci 数列的第 n 项 an,如果 an 位数 ≤8 ,否则按格式“高四位…低四位”输出。
分析:注意点:题目给的 n 的范围是 [0, 108] ,直接使用递推的方式求 an 会超时,并且数值超出了表示范围(C++)。
首先Fibonacci 数列的 0~39 项的位数都是在 8 位以内的,如果不知道,可以观察测试用例,a39 对应 63245986 , a40 对应 1023…4155 。所以,对于 0~39 项,可以通过递推公式先打表。
然后,先考虑取出高四位。
Fibonacci 数列有如下的通项公式:
对通项公式进行变换(两边取对数)得到下面的表达式:
以10为底取对数的好处在于,如果将大数用科学计数法的形式表示,例如 123456789123456789 。就可以写成 1.23456789123456789x1017 , 进一步可以写成 log10(1.23456789123456789)+17 这种小数加整数的形式,即 log10(123456781456789) = log10(1.23456789123456789)+17 ,如果想得到高四位,就是将 10log10(1234.56789123456789)向下取整即可,而 log10(1234.56789123456789) = log10(1.23456789123456789)+3 ,就和log10(123456789123456789) 建立了联系。按照这种思路,一个大数的高四位就容易得到了。
对于低四位,取模就可以,但是因为递推会超时,所以需要用到下面的性质:
这样就是矩阵快速幂+取模,在找到对应位置的值就可以了。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef struct{
int a[2][2];
}MATRIX;
const int mod = 10000;
MATRIX a, ans;
int k;
int f[40];
void init(){
f[1] = 1;
f[2] = 1;
for(int i=3; i<40; i++){
f[i] = f[i-1] + f[i-2];
}
}
MATRIX mult(MATRIX a, MATRIX b){
MATRIX tmp;
for(int i=0; i<2; i++){
for(int j=0; j<2; j++){
tmp.a[i][j] = ((a.a[i][0]*b.a[0][j])%mod + (a.a[i][1]*b.a[1][j])%mod)%mod;
}
}
return tmp;
}
MATRIX fastPow(MATRIX m, int n){
MATRIX res;
for(int i=0; i<2; i++){
res.a[i][0] = res.a[i][1] = 0;
res.a[i][i] = 1;
}
while(n){
if(n&1) res = mult(res, a);
a = mult(a, a);
n >>= 1;
}
return res;
}
int head(int n){
double t=-0.5*log10(5.0)+(double)n*log10((1+pow(5.0,0.5))/2);
t=t-floor(t); //获得小数部分
double x=pow(10,t+3); //还原
return (int)floor(x); //得到整数部分
}
int main(){
init();
while(~scanf("%d", &k)){
if(k<40) printf("%d\n", f[k]);
else{
a.a[0][0] = 0;
a.a[0][1] = a.a[1][0] = a.a[1][1] = 1;
printf("%d...", head(k));
ans = fastPow(a, k-1);
printf("%04d\n", ans.a[1][1]);
}
}
return 0;
}