题目描述
斐波那契数列大家都非常熟悉。它的定义是:
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
样例输出
0
基本思路:
1,费波拉契数列:
斐波那契数列矩阵的定义如下:F=[[1, 1],[1, 0]]斐波那契数列矩阵的每个元素都是斐波那契数列中的一个数。例如,F[0][0] =1,F[0][1]= 1,F[1][0]= 1,F[1][1]= 0。
斐波那契数列矩阵有一个非常有趣的性质,它可以用来计算斐波那契数列中的任意一项。具体来说,如果我们想要计算斐波那契数列中的第n项,我们可以用以下公式: F^n= [[Fn+1, Fn], [Fn, Fn-1]]其中,Fn表示斐波那契数列中的第n项。例如,如果我们想要计算斐波那契数列中的第10项,我们可以用以下公式: F^10= [[Fn+1, Fn], [Fn, Fn-1]] = [[F11, F10], [F10, F9]] = [[89, 55],[55, 34]]因此,斐波那契数列中的第10项是55。
推理过程如图:
2,费波拉契求和公式:
3,解题思路:
4,注意事项:
由于输入的数字可能非常大,为了减少编译时间,提高编译效率,采用快速幂运算来得到矩阵的n次方
个人实现:
(不是完全正确,仅用于解释)
#include<stdio.h>
#include<string.h>
typedef long long ll;//长整型
typedef struct//定义矩阵
{
ll a[2][2];//该二维矩阵的n次方可以表示f(n+1)*f(n-1)-f(n)*f(n)
}matrix;
ll n, m, p;
ll fib_m;
ll mul_mod(ll a, ll b, ll p)//大数相乘并取模
{
ll ans = 0;
while (b)
{
if (b & 1)//检查b的最后一位是否为1
{//如果b的最后一位是1,相当于a乘了一个奇数
ans = ans + a;//需要再加一个a
ans = ans % p;
}
a << 1;//a乘2对p继续取模
a=a% p;
b >> 1;//b除2
}
return ans;
}
matrix mul_mat_l(matrix frist, matrix second)//两矩阵相乘(模P)
{
matrix result;
memset(result.a, 0, sizeof(result.a));
//memset函数用于将一块内存区域设置为特定的值
//此处时将数组a的所有元素设置为0
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 2; j++)
{
for (int k = 0; k < 2; k++)//遍历第一个输入矩阵的列和第二个输入矩阵的行
{
result.a[i][j] = (result.a[i][j] % p + mul_mod(frist.a[i][k], second.a[k][j], p) % p) % p;
//进行矩阵乘法时,每一行元素都需要进行独立运算
//不取模可能影响后面计算导致错误,这样可以确保每行计算结果独立性
}
}
}
}
matrix POW_1(matrix mat, ll n)//矩阵的n次方
{
matrix t;
t.a[0][0] = 1;
t.a[0][1] = 0;
t.a[1][0] = 0;
t.a[1][1]= 1;
while (n > 0)//快速幂运算,直到n变为0
{
if (n & 1)
{
t = mul_mat_l(t, mat);//是奇数,再乘一次自身
}
n=n >> 1;
mat = mul_mat_l(mat, mat);//n为偶数,平方变为原来的四倍
//减少循环次数,加速计算
}
}
ll quick_fib_l(ll c)//费波拉契矩阵的n次方
{
matrix mat, temp;
memset(mat.a, 0, sizeof(mat.a));
mat.a[0][0] = 1;
mat.a[0][1] = 1;
mat.a[1][0] = 1;
temp = POW_1(mat, c);
return temp.a[0][1];
}
/以上都是基于对p取模的优化办法
下面是一般情况下的正常做法
matrix mul_mat(matrix first, matrix second)
{
matrix result;
memset(result.a, 0, sizeof(result.a));
for (int i = 0; i < 2; i++)//结果的行
{
for (int j = 0;j < 2; j++)//结果的列
{
for (int k = 0; k < 2; k++)//第一个数列的列和第二个数列的行
{
result.a[i][j] = (result.a[i][j] + first.a[i][k] * second.a[k][j]);
}
}
}
return result;
}
matrix POW(matrix mat, ll n)//数列的n次方
{
matrix t;//设置一个单位矩阵
t.a[0][0] = 1;
t.a[0][1] = 0;
t.a[1][0] = 0;
t.a[1][1] = 1;
while (n > 0)
{
if (n & 1)//是奇数时
{
t = mul_mat(t, mat);
}
n=n >> 1;
mat = mul_mat(mat, mat);
}
return t;
}
ll quick_fib(ll c)//费波拉契数列的n次方
{
matrix mat, temp;//mat是费波拉契数列,需要初始赋值
memset(mat.a, 0, sizeof(mat.a));//对数组初始化
mat.a[0][0] = 1;
mat.a[0][1] = 1;
mat.a[1][0] = 1;//mat.a[1][1]=0;
temp = POW(mat, c);
return temp.a[0][1];
}
int main()
{
scanf("%lld %lld %lld", &n, &m, &p);
if (m > n + 2)//m大于n+2,不需要计算f(m)
{
printf("%lld", (quick_fib_l(n + 2)) % p - 1);
return 0;
}
else//否则需要模f(m)
{
fib_m = quick_fib(m);//该数不可以取模
printf("%lld", (quick_fib(n + 2)) % fib_m % p - 1);
//此时n+2<m,m的费波拉契数可以算出,n+2的也可以
}
return 0;
}
实例代码:
#include <stdio.h>
#include <string.h>
typedef long long ll;
typedef struct //矩阵结构体
{
ll a[2][2];
}matrix;
ll n,m,p;
ll fib_m;
ll mul_mod(ll a,ll b,ll p) //大数相乘取模
{
ll ans=0;
while(b)
{
if(b&1)
{
ans=ans+a;
ans=ans%p;
}
a=a<<1;
a=a%p;
b=b>>1;
}
return ans;
}
matrix mul_mat_1(matrix first,matrix second) //矩阵乘法
{
matrix result;
memset(result.a,0,sizeof(result.a)); //初始化
for(int i=0;i<2;i++)
{
for(int j=0;j<2;j++)
{
for(int k=0;k<2;k++)
{
result.a[i][j]=(result.a[i][j]%p+mul_mod(first.a[i][k],second.a[k][j],p)%p)%p;
}
}
}
return result;
}
matrix POW_1(matrix mat,ll n) //矩阵mat的n次方
{
matrix t;
t.a[0][0]=1; //单位矩阵
t.a[0][1]=0;
t.a[1][0]=0;
t.a[1][1]=1;
while(n>0) //快速幂
{
if(n&1) //如果是奇数
{
t=mul_mat_1(t,mat); //等于结果乘当前的底数
}
n=n>>1;
mat=mul_mat_1(mat,mat); //自身平方
}
return t;
}
ll quick_fib_1(ll c) //c代表次方数
{
matrix mat,temp;
memset(mat.a,0,sizeof(mat.a));
mat.a[0][0]=1;
mat.a[0][1]=1;
mat.a[1][0]=1;
temp=POW_1(mat,c);
return temp.a[0][1];
}
matrix mul_mat(matrix first,matrix second) //矩阵乘法
{
matrix result;
memset(result.a,0,sizeof(result.a)); //初始化
for(int i=0;i<2;i++)
{
for(int j=0;j<2;j++)
{
for(int k=0;k<2;k++)
{
result.a[i][j]=(result.a[i][j]+first.a[i][k]*second.a[k][j]);
}
}
}
return result;
}
matrix POW(matrix mat,ll n) //矩阵mat的n次方
{
matrix t;
t.a[0][0]=1; //单位矩阵
t.a[0][1]=0;
t.a[1][0]=0;
t.a[1][1]=1;
while(n>0) //快速幂
{
if(n&1) //如果是奇数
{
t=mul_mat(t,mat); //等于结果乘当前的底数
}
n=n>>1;
mat=mul_mat(mat,mat); //自身平方
}
return t;
}
ll quick_fib(ll c) //c代表次方数
{
ll ans;
matrix mat,temp;
memset(mat.a,0,sizeof(mat.a));
mat.a[0][0]=1;
mat.a[0][1]=1;
mat.a[1][0]=1;
temp=POW(mat,c);
return temp.a[0][1];
}
int main()
{
scanf("%lld %lld %lld",&n,&m,&p);
if(m>n+2) //如果m大于n+2,不需要计算f(m)的值
{
printf("%lld",(quick_fib_1(n+2))%p-1);//可以中途模p
return 0;
}
else //否则需要模上f(m)
{
fib_m=quick_fib(m); //算出fibm的值。这个数没办法取模,只能硬算
printf("%lld",(quick_fib(n+2))%fib_m%p-1);//第m项可以不优化求出来 ,所以第n+2项也可以不取模优化
}
return 0;
}