C语言实现 蓝桥杯 算法训练 小X的暑假作业

试题 算法训练 小X的暑假作业

                                                                                  蓝桥杯试题解答汇总链接

资源限制

       时间限制:1.0s 内存限制:128.0MB


问题描述

       小X的作业是老师给他布置的,题目很简单,求出第N个斐波那契数 mod P的值。小X怎么想也想也想不出来,只好求助于你了。


输入格式

       第一行为数据组数T。
  第二行开始,以下T行,每行为老师给小X布置的作业中的N和P。


输出格式

       包含T行,每行是一个作业的答案。


样例输入
3
7 3
1000000 89
987654321 30000

样例输出
0
55
19111

数据规模与约定
对于50%的数据,T<=100,N<=10000。
对于100%的数据,T<=10000,N<=1000000000,P<=30000。
斐波那契数的第0个是1。

试题解析

斐波那契数列递推式为:f(n + 2) = f(n + 1) + f(n)(n为非负整数)
使用滚动数组去求会超时,下面我用的时矩阵的快速幂的方法。
mat代表矩阵,E为单位矩阵,B为转移矩阵。
根据递推式建立矩阵An=[f(n + 1), f(n)] (易得A0=[f(1), f(0)]=[1, 1]),下面还需要转移矩阵B使得
AnB=[f(n + 2), f(n + 1)]=An+1,那么An=A0Bn,根据递推式可得转移矩阵B如下
在这里插入图片描述
即问题变成求矩阵B的幂的问题了对矩阵B快速幂即可,最后与A0对应相乘即可


代码
#include <stdio.h>
typedef long long ll;
typedef struct ma{
	ll m[2][2];
}mat;
ll mod;
mat E = {0}, B = {0};
mat sq(mat a, mat b) {//求矩阵a*矩阵b
	mat c;
	int i, j, k;
	for(i = 0;i < 2; ++i) {
		for(j = 0;j < 2; ++j) {
			c.m[i][j] = 0;
			for(k = 0;k < 2; ++k)
				c.m[i][j] = (c.m[i][j] + a.m[i][k] * b.m[k][j]) % mod;
		}
	}
	return c;
}
mat qpow(mat a, ll n) {// 求矩阵a的n次方
	mat ans = E;
	for(;n; n >>= 1, a = sq(a, a)) {
		if(n&1) ans = sq(ans, a);
	}
	return ans;
}
int main() {
	ll t, i, n, num[10000];
	scanf("%lld", &t);
	E.m[0][0] = E.m[1][1] = 1;
	for(i = 0;i < t; ++i) {
		B.m[0][0] = B.m[1][0] = B.m[0][1] = 1;
		B.m[1][1] = 0;
		scanf("%lld%lld", &n, &mod);
		mat ans = qpow(B, n - 1);
		num[i] = (ans.m[0][0] + ans.m[1][0]) % mod;
	}
	for(i = 0;i < t; ++i) 
		printf("%lld\n", num[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Derrick-Xu

谢大哥打赏,我会继续努力!

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

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

打赏作者

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

抵扣说明:

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

余额充值