C語言實現 藍橋杯 歷屆試題 斐波那契

試題 歷屆試題 斐波那契

                                                                                  藍橋杯試題解答彙總鏈接

資源限制

       時間限制: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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章