蓝桥杯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 需要注意
同时需要注意输入范围