[紫书CH10] 例题10-1:巨大的斐波拉契数(fib循环节、模算术、找规律、UVa11582)

紫书题解汇总:[紫书CH0] 《算法竞赛入门经典》(第2版) 题解目录

1. 题目来源

链接:UVA11582 Colossal Fibonacci Numbers!

2. 题目说明

在这里插入图片描述
中文描述:

输入两个非负整数 ab 和正整数 n ( 0 ≤ a , b < 2 64 , 1 ≤ n ≤ 1000 ) (0≤a,b <2^{64},1≤n≤1000) (0a,b<264,1n1000),你的任务是计算 f ( a b ) f(ab) f(ab)除以 n 的余数。其中 f ( 0 ) = f ( 1 ) = 1 f(0)=f(1)=1 f(0)=f(1)=1,且对于所有非负整数 i f ( i + 2 ) = f ( i + 1 ) + f ( i ) f(i+2)=f(i+1)+f(i) f(i+2)=f(i+1)+f(i)

3. 题目解析

方法一:fib循环节+模算术+找规律

竞赛题与一般的面试笔试题的最大不同点在于它的数据范围。在 LeetCode 上大多都不会出现爆 int 的情况,即数据不够强,但是这个题目所要求的 2^64 显然已经大大超出了 int 的范围。那么 long 够吗?long long 够吗?其实,都不够…

int-21474836482147483647 (10位数,2e9 2^31 - 1)
unsigned int04294967295 (10位数,4e9)
long long-92233720368547758089223372036854775807 (19位数, 9e18 ) 2^63 - 1
unsigned long long018446744073709551615 (20位数,1e19) 2^64 - 1

所以在此 a、b 的数据类型得是 unsigned long long 才可以。采用 scanf、printf 读取输出时需要使用 %llu 或者 %I64u

主要思想就是打表+fib循环节lrj 大佬在书中提到对 n 取模,余数最多 n 种,所以最多 n^2 种余数组合情况,也就是在 n^2 项内就会出现循环节,导致后面的进行重复。但好像用不到 n^2 那么多项,fib 取模循环节我也不太了解,甚至今天第一次知道…关键就是二维数组的大小开辟选择,不放心的话就直接 MAXN * MAXN 好点,反正找到了就会 break 掉。并且在这找的循环节还是 0、1 循环节,那要是找找其他 fib(n-1)、fib(n) 呢?想想确实很有意思哈哈。

还有一点就是 pow_mod 幂取模 也就是简单的 快速幂取余 的使用,也是 lrj 大佬在前面刚刚讲过的小知识点。

参见代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 1e3 + 50;
#define ULL unsigned long long

int f[MAXN][MAXN * 7], F[MAXN];

int pow_mod(ULL a, ULL b, int n) {
	if (b == 0) return 1;
	int x = pow_mod(a, b / 2, n);
	int res = (ULL)x * x % n;
	if (b % 2 == 1) res = res * a % n;
	return res;
}

int solve(ULL a, ULL b, int n) {
	if (a == 0 or n == 1) return 0;
	int x = pow_mod(a % F[n], b, F[n]);
	return f[n][x];
}

int main() {
	// memset(f, 0, sizeof(f));
	// memset(F, 0, sizeof(F));
	for (int n = 2; n <= 1000; ++n) {
		f[n][0] = 0, f[n][1] = 1;
		for (int i = 2; ; ++i) {
			f[n][i] = (f[n][i - 1] + f[n][i - 2]) % n;
			if (f[n][i - 1] == 0 and f[n][i] == 1) {
				F[n] = i - 1;
				break;
			}
		}
	}

	int t;
	scanf("%d", &t);
	ULL a, b;
	int n, res;

	while (t--) {
	    cin >> a >> b >> n;
	    cout << solve(a, b, n) << "\n";
	}
	/*
	// 一开始采用c语言形式输出未包含using namespace std;本地能过但是过不去oj,不知什么情况...
	// 靠,找到原因了,这个I64u在OJ上不支持,貌似只能支持llu,无语了...
	while (t--) {
		scanf("%llu%I64u%d", &a, &b, &n);
		res = solve(a, b, n);
		printf("%d\n", res);
	}
	*/
	return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Ypuyu

如果帮助到你,可以请作者喝水~

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

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

打赏作者

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

抵扣说明:

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

余额充值