題目鏈接
題目大意:輸出 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;
}