蓝桥杯2014c++A组真题&代码第八题斐波那契 矩阵乘法 快速幂 快速乘法

蓝桥杯2014c++A组真题&代码第八题斐波那契   矩阵乘法 快速幂 快速乘法

 

/*
标题:斐波那契

    斐波那契数列大家都非常熟悉。它的定义是:

    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) 取模。
    公式参见【图1.png】

    但这个数字依然很大,所以需要再对 mod 求模。

【数据格式】
输入为一行用空格分开的整数 n m mod (0 < n, m, mod < 10^18)
输出为1个整数

例如,如果输入:
2 3 5
程序应该输出:
0

再例如,输入:
15 11 29
程序应该输出:
25

资源约定:
峰值内存消耗 < 256M
CPU消耗  < 1000ms


请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include <xxx>, 不能通过工程设置而省略常用头文件。

提交时,注意选择所期望的编译器类型。
*/
//f(x) = f(x-1) + f(x-2) -->f(x+1)=f(x)+f(x-1)-->f(x)=f(x+1)-f(x-1)
//Σf(n)=f(n+2)-1
//原题等价于(f(n+2)-1)%f(m)%mod 等价于f(n+2)%f(m)%mod-1
// --> 如果m>=n+2,余f(m)没意义,等价于f(n+2)%mod-1
//-->  否则,一定要求f(m)
#include<iostream>
#include<cstring>
#include<algorithm> 
#include<vector>
using namespace std;
typedef unsigned long long ll;
typedef vector<ll> vec;
typedef vector<vec> mat; 
const ll MAXN = 1e18;

ll N,M,P;

//快速乘法
ll mm(ll a, ll b, ll mod) {
    if (a > b) {
        ll t = a;
        a = b;
        b = t;
    }
    ll x = 0;
    while (b != 0) {
        if ((b & 1) == 1) {
            x = (x + a) % mod;
        }
        a = (a * 2) % mod;
        b >>= 1;
    }
    return x;
}


mat mul(mat &A, mat &B,ll mod ){
	mat C(A.size(),vec(B[0].size()));
	
	for(int i=0;i<A.size();i++){
		for(int k=0;k<B.size();k++){
			for(int j=0;j<B[0].size();j++){
				if(mod >0)
				C[i][j] = (C[i][j] % mod+ mm(A[i][k],B[k][j],mod))%mod;
				else
				C[i][j] = (C[i][j]+ A[i][k]*B[k][j]);
			}
		}
	}
	return C;
}


void output(vector<vector<ll> > a){
	
	printf("------------------\n");
	printf("shap :%d %d\n",a.size(),a[0].size());
	for(int i=0;i<a.size();i++){
		for(int j=0;j<a[0].size();j++){
	    	printf("%lld ", a[i][j]);
		}
		printf("\n");
	}
}

mat pow (mat a ,ll n, ll mod){
	
	mat B( a.size(),vec(a[0].size()));
	for(int i=0;i<B.size();i++){
		B[i][i] = 1;
	}
	while(n>0){
        if(n & 1)B = mul(B, a, mod);	
//        output(B);
		a=mul(a, a,mod);	
		n>>=1;
	}
	return B;
} 

ll f(ll num ,ll mod){
	if(num == 1 || num==2) return 1;
	mat B( 2,vec(2));
	B[0][0] = 1;	B[1][1] = 0;
	B[0][1] = 1;	B[1][0] = 1;
//	output(B);
	mat res = pow(B,num ,mod);
//    output(res);
	return res[1][0];
}

//  错误总结,没有注意输入的范围用了int 
int main(){
	
	scanf("%llu%llu%llu",&N,&M,&P);
    
    ll mod = P;
    if(M>=N+2){
    	ll m_ =  f(N+2,mod);
    	printf("%llu\n",m_-1);
	} else if(M<N+2){
    	//注意 对于 f(M) 的计算,这里是不能带mod 的 否则会影响最终的结果 
    	mod = f(M, -1);
    	ll m_ =  f(N+2,mod); 
//	m_--;
//	ll m_ =  f(N+2,P); m_--; 先后取两个模,貌似不能交换顺序 
    	printf("%llu\n",m_%P-1);
	}
	return 0;
} 

 另外,这里有一个猜测性结论,当先后取两个不同的模时,交换先后顺序会对最后的结果产生影响。

这里也给了 案例  比如 

56  %3%4  为 2

而  56%4%3 为 0  需要注意

 

同时需要注意输入范围

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值