试题 历届试题 斐波那契
资源限制
时间限制: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;
}