蓝桥杯-斐波那契(快速矩阵幂)

历届试题  斐波那契    
时间限制: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  (0  <   n,  m,  p  <   10^18) 
输出格式 
  输出为1个整数,表示答案 
样例输入 
2  3  5 
样例输出 

样例输入 
15  11  29 
样例输出 
25 

思路:S(n)=f(n+2)-1

原式=(f(n+2)-1)%f(m)%p  再利用矩阵快速幂求f()

Code:

#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;

LL llmul( LL a,LL b,LL mod ) {
	a%=mod;a+=mod;a%=mod;
	b%=mod;b+=mod;b%=mod;
	if ( a<b )	swap( a,b );
	LL ret=0;
	while ( b ) {
		if ( b&1 )ret=( ret+a )%mod;
		a=( a<<1 )%mod;
		b/=2;
	}
	return ret;
}
struct matrix {
    LL x[3][3];
    matrix() {memset( x,0,sizeof x );}
};
matrix mmul( matrix &A,matrix &B,LL mod ) {
    matrix ret;
    for ( int i=1; i<=2; i++ )
		for ( int j=1; j<=2; j++ )
			for ( int k=1; k<=2; k++ )
        	ret.x[i][j]=( ret.x[i][j]+llmul( A.x[i][k],B.x[k][j],mod ) )%mod;
    return ret;
}
matrix E,A;
LL fib( LL n,LL mod ) {
    matrix O=E,B=A;
    while ( n ) {
        if ( n&1 )O=mmul( O,B,mod );
        B=mmul( B,B,mod );
        n/=2;
    }
    return O.x[1][2];
}
LL solve( LL n,LL m,LL mod ) {
    //f(n)%f(m)%mod
    LL t=n/m;
    //(f(m-1)^t*f(n%m))%f(m)%mod;
    //f(i)^2%f(i-1)=(-1)^(i+1)
    LL p=t/2,q=t%2;
    //{f(m-1)^q*(-1)^(pm)*f(n%m)}%f(m)%mod
    LL fuhao=p*m%2==0?1:-1;
    if ( q==0 ) {
        LL ans=fib( n%m,mod )*fuhao;
        ans%=mod;
        ans+=mod;
        return ans%mod;
    }
    if ( n%m==0 )return 0;
    //f(m-1)*f(n%m)*fuhao%f(m)%mod
    //x%y%mod=(x-a*y)%mod
    //a=[x/y]
    LL x=(llmul(fib(n%m,mod),fib(m-1,mod),mod)*fuhao%mod+mod)%mod;
    LL y=fib(m,mod);
    LL a=fib(n%m-1,mod);
    if(n%m%2==0)	a--;
    if(fuhao<0)	a++;
    a=(a%mod+mod)%mod;
    return ((x-llmul(a,y,mod))%mod+mod)%mod;
}

int main ()
{
    A.x[1][2]=A.x[2][1]=A.x[2][2]=1;
    E.x[1][1]=E.x[2][2]=1;
    LL n,m,mod;
    while ( cin>>n>>m>>mod )
        cout<<( solve( n+2,m,mod )-1+mod )%mod<<endl;
    return 0;
}

 

code:

#include <iostream>  
using namespace std;  
typedef long long LL; 

const int N = 2; 
LL MOD;
struct Matrix{
    LL m[N][N];  
};  
Matrix I = {  
    1, 0,
    0, 1
};
Matrix A = {
    1, 1,
    1, 0
};
  
Matrix multi(Matrix A, Matrix B)  
{  
    Matrix C;  
    for(int i = 0; i < N; i++)  
        for(int j = 0; j < N; j++)  
        {  
            C.m[i][j] = 0;  
            for(int k = 0; k < N; k++)  
                C.m[i][j] += A.m[i][k] * B.m[k][j];  
            C.m[i][j] %= MOD;  
        }  
    return C;  
}  
  
Matrix Power(Matrix A, LL n)  
{  
    Matrix ans = I, P = A;  
    while(n)  {  
        if(n & 1){  
            ans = multi(ans, P);  
            n--;  
        }
        n >>= 1;  
        P = multi(P, P);  
    }
    return ans;  
}  
  
//计算F(n) % MOD  
LL getFun(LL n)  
{  
    Matrix ans = Power(A, n);  
    return ans.m[1][0];
}  
  
//计算F(m - 1) * F(n % m) mod F(m)  
LL getRes(LL n, LL m)  
{  
    LL k = n % m;  
    if(k & 1)  
        return getFun(m - k);  
    return ((getFun(m) - getFun(m - k)) % MOD + MOD) % MOD;  
}  
  
LL Solve(LL n, LL m)  
{  
    LL t1 = n / m;  
    if(m & 1){  
        LL t2 = t1 >> 1;  
        if(t1 % 2 == 0 && t2 % 2 == 0)  
            return getFun(n % m);  
        if(t1 % 2 == 0 && t2 % 2 == 1)  
            return ((getFun(m) - getFun(n % m)) % MOD + MOD) % MOD;  
        if(t1 % 2 == 1 && t2 % 2 == 0)  
            return getRes(n, m);  
        if(t1 % 2 == 1 && t2 % 2 == 1)  
            return ((getFun(m) - getRes(n, m)) % MOD + MOD) % MOD;  
    }else{  
        if(t1 & 1)	return getRes(n, m);  
        else    return getFun(n % m);  
    }
}

LL getResponse(LL n, LL m)  
{
    LL res = Solve(n, m);  
  if(res == 0)	//f(n)之和 
      return getFun(m) - 1;
  return res - 1;
//    return res;
}  
  
int main()
{
	ios::sync_with_stdio(false);
 	LL n, k;
    while(cin>>n>>k>>MOD){
        cout<<getResponse(n+2, k)<<endl;
    }
    return 0;
}  

 

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值