未来计算 3689 未来算算 1730 佳佳的 Fibonacci

PS:如果读过题了可以跳过题目描述直接到题解部分

提交链接1:未来计算 3689 佳佳的 Fibonacci

提交链接2:未来算算 1730 佳佳的 Fibonacci

题目

题目描述

佳佳对数学,尤其对数列十分感兴趣。在研究完 Fibonacci 数列后,他创造出许多稀奇古怪的数列。例如用 S(n) 表示 Fibonacci 前 n 项和 mod m 的值,即 S(n)=(F1 +F2 +...+Fn ) mod m,其中 F1 =F2 =1,Fi =Fi−1 +Fi−2 。可这对佳佳来说还是小菜一碟。

终于,她找到了一个自己解决不了的问题。用 T(n)=(F1 +2F2 +3F3 +...+nFn ) mod m 表示 Fibonacci 数列前 n 项变形后的和 mod m 的值。

现在佳佳告诉你了一个 n 和 m,请求出 T(n) 的值。

输入

输入数据包括一行,两个用空格隔开的整数 n,m。

输出

仅一行,T(n) 的值。

样例

输入

5 5

输出

1

提示

样例解释

T(5)=(1+2×1+3×2+4×3+5×5) mod 5=1

数据范围与提示

对于 30% 的数据,1≤n≤1000;

对于 60% 的数据,1≤m≤1000;

对于 100% 的数据,1≤n,m≤2^31 −1。

题解

矩阵乘法

具体内容详见我之前的文章:矩阵乘法

推导矩阵

其实吧,看到是斐波那契,多练过几道题的都知道是考矩阵乘法,但难的地方在于矩阵怎么写。

对于一般的斐波那契数列,我们用来推单项的矩阵通常都长这样:

 但对于这道题的这个矩阵我们先要分析这个问题的构造。

因为我们是从前往后递推(显然的废话),我一开始的想法是用一个矩阵在更新原矩阵的时候再用另一个矩阵来更新那个矩阵对于原矩阵的数乘的值(我知道很绕,所以这一句话看不懂也没关系)

但这样就不能使用快速幂了。

所以我换了一个思路,用全部的和来减。大概是这样:

 最后用第二排的乘 n 以后减去第一排的那个就是答案了。

“正解”

这道题其实还有一个做法,被称为正解。

就是当我们假设S_{n}=\sum_{i=1}^{n}F_{i}时,我们会发现S_{n}=F_{n+2}-1

所以我们可以把 T(n) 反复代换,最后T\left ( n \right )=n*F_{n+2}-F_{n+3}+2

相当于就可以只需要算出两个斐波那契的单项就可以了。

(但我个人认为,反正都是用矩阵快速幂,两种好像都差不多)

代码实现

//未来计算 3689 未来算算 佳佳的 Fibonacci
#include<iostream>
#include<cstdio>
using namespace std;
long long n,m;
long long a[6][6];
long long b[6][6];
long long c[6][6];
long long ans;
long long x;

int main(){
	scanf("%lld%lld",&n,&m);
	a[0][0]=a[0][1]=a[1][1]=a[1][2]=a[2][3]=a[3][2]=a[3][3]=1;
	for(int i=0;i<=3;++i){
		for(int j=0;j<=3;++j){
			b[i][j]=(i==j);
		}
	}
	x=n;
	while(n){
		if(n&1){
			for(int i=0;i<=3;++i){
				for(int j=0;j<=3;++j){
					for(int k=0;k<=3;++k){
						c[i][j]=(c[i][j]+(b[i][k]%m*a[k][j]%m)%m)%m;
					}
				}
			}
			for(int i=0;i<=3;++i){
				for(int j=0;j<=3;++j){
					b[i][j]=c[i][j];
					c[i][j]=0;
				}
			}
		}
		for(int i=0;i<=3;++i){
			for(int j=0;j<=3;++j){
				for(int k=0;k<=3;++k){
					c[i][j]=(c[i][j]+(a[i][k]%m*a[k][j]%m)%m)%m;
				}
			}
		}
		for(int i=0;i<=3;++i){
			for(int j=0;j<=3;++j){
				a[i][j]=c[i][j];
				c[i][j]=0;
			}
		}
		n>>=1;
	}
	ans=((((b[1][2]+b[1][3])%m*x%m)%m-(b[0][2]+b[0][3])%m)%m+m)%m;
	printf("%lld\n",ans);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月半流苏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值