試題 歷屆試題 斐波那契
資源限制
時間限制:1.0s 內存限制:256.0MB
問題描述
斐波那契數列大家都非常熟悉。它的定義是:
f(x) = 1 … (x=1,2)
f(x) = f(x-1) + f(x-2) … (x>2)
對於給定的整數 n 和 m,我們希望求出:
f(1) + f(2) + … + f(n) 的值。但這個值可能非常大,所以我們把它對 f(m) 取模。
公式如下
但這個數字依然很大,所以需要再對 p 求模。
輸入格式
輸入爲一行用空格分開的整數 n m p
輸出格式
輸出爲1個整數,表示答案
樣例輸入
樣例一:
2 3 5
樣例二:
15 11 29
樣例輸出
樣例一:
0
樣例二:
25
數據規模與約定
0 < n, m, p < 10^18
試題解析
公式爲f(1)=f(2)=1,②f(n+2)=f(n+1)+f(n)(n爲正整數)
設s(n)爲數列f(n)的前n項和,易得:
f(n+2)=f(n+1)+f(n)
f(n+1)=f(n)+f(n-1)
f(n)=f(n-1)+f(n-2)
…
f(4)=f(3)+f(2)
f(3)=f(2)+f(1)
把這n個等式相加左邊=s(n+2)-f(2)-f(1);右邊=s(n+1)-f(1)+s(n);
整理得s(n+2)=s(n+1)+s(n)+f(2)即s(n)=f(n+2)-1(n爲正整數)
所以我們現在只需要求①(f(n+2)-1)modf(m)modp
觀察①式可知當n+2≤m時f(n+2)-1<f(m)此時直接計算f(n+2)modp即可
但當n+2>m時就需要先計算f(m)但由題目給出範圍來看0<m<1018直接求f(m)會超時,這時候就需要轉移:注意到公式②是兩個相加得到一個所以構造一個2×2的矩陣A和轉移矩陣B(其實矩陣A是1×2矩陣就夠了但爲了方便思路我就用2×2)
由上圖可得要求f(n)即求A×Bn-1次方所以求出Bn-1又f(1)=f(2)=1所以最後的結果就是Bn-1的第一行兩個數的和,計算矩陣的冪可以使用快速冪的方法縮短計算時間,爲了防止溢出在計算過程中就進行取餘操作,最後輸出時在進行一次取餘就是要求的結果
代碼
#include<stdio.h>
typedef unsigned long long ll;
typedef struct _m{
ll m[2][2];
}mat;
/**快速乘法
* 因爲數據範圍太大所以快速乘對大的進行分解
*/
ll mm(ll x,ll y,ll mod){
if(x>y){
ll t=x;
x=y;
y=t;
}
ll r=0;
while(y){
if(y&1){
r=(r+x)%mod;
}
x=(x*2)%mod;
y>>=1;
}
return r;
}
/** 求矩陣的平方
* 初始化矩陣c的元素爲0
* 判斷是否需要進行快速乘法
* 兩次相加在取模縮小數據防止超出範圍
*/
mat mul(mat a,mat b,ll mod){
mat c;
int i,j,k;
for(i=0;i<2;++i){
for(j=0;j<2;++j){
c.m[i][j]=0;
for(k=0;k<2;++k){
if(mod){
c.m[i][j]+=mm(a.m[i][k],b.m[k][j],mod);
c.m[i][j]%=mod;
}else{
c.m[i][j]+=a.m[i][k]*b.m[k][j];
c.m[i][j]%=mod;
}
}
}
}
return c;
}
/**矩陣快速冪
* 初始化轉移矩陣B和單位矩陣E
* 求矩陣b的n次方且每個元素都是對mod取餘
* 初始化ans爲單位矩陣
* 每次給b平方一次n也就除以2
* 奇數就多乘一次
*/
mat B={0,1,1,1},E={1,0,0,1};
mat qpow(mat b,ll n,ll mod){
mat ans=E;
for(;n;n>>=1,b=mul(b,b,mod)){
if(n&1){
ans=mul(ans,b,mod);
}
}
return ans;
}
/**求f(n)%mod
* 小於3直接返回值
* 求B矩陣的n-1次方並對mod取餘
* 最後結果就是A矩陣的第一個元素
*/
ll f(ll n,ll mod){
if(n<3)return 1;
mat r=qpow(B,n-1,mod);
ll res=(r.m[0][0]+r.m[1][0])%mod;
return res;
}
int main(){
ll n,m,p,mod,res;
scanf("%llu%llu%llu",&n,&m,&p);
/**
* n+2<=m就沒有必要對f(m)取餘了之間對p取餘即可
*/
if(n+2<=m){
res=f(n+2,p);
printf("%llu",(res-1)%p);
return 0;
}
/**
* 先求出f(m)然後在計算f(n+2)%f(m)
* 最後在對p取餘
*/
mod=f(m,-1);
res=f(n+2,mod);
printf("%llu",(res-1)%p);
return 0;
}