UVA11582- Colossal Fibonacci Numbers! 斐波那契数列循环节问题

题目意思很简单,给定a、b、n,求斐波那契数列第ab项模n的值,那么如何考虑呢?

  • 首先我们应该知道求斐波那契数列的任意一项可以使用矩阵快速幂,速度很快,但是对于这道题,由于a和b都非常大,所以不能使用这种方法,考虑到最后都要模一个n,考虑斐波那契数列会出现循环节,如果要问为什么,因为斐波那契数列连续两项和等于后一项,所以如果出现和最开始相同的两项,那么就说明找到了循环的位置,而且这循环节一定存在
  • 为什么?可以这样考虑,根据斐波那契数列的性质,设数列为A,如果能够找到Ai mod n = Ak mod n并且Ai+1 mod n = Ak+1 mod n,那么Ai到Ak-1就是一个循环节,周期为k-i,那么每一个数都模n,相邻两项只有n * (n - 1)种情况,那么根据抽屉原理(鸽巢原理),最多寻找n * (n - 1) + 1项就会找到这个循环节
  • 所以可以看出随着n的不同,斐波那契循环节在发生变化,且对于每一个n应该有唯一一个循环节,也就是说这是一个周期函数的问题,这样我们就可以利用周期将任意一个位置化成一个最简单的位置。这道题的n范围是1000,时间也是可以接受的
#include <iostream>
using namespace std;
typedef unsigned long long ull;
const int MAXN = 1e6 + 100;
ull Data[MAXN];
int fastpow(ull base, ull power, int MOD){
    
    
	ull ans = 1;
	while(power){
    
    
		if(power & 1) ans = ans * base % MOD;
		base = base * base % MOD;
		power >>= 1;
	}
	return ans % MOD;
}
ull solve(ull a, ull b, int n){
    
    
	Data[0] = 0;
	Data[1] = 1;
	int len;
	int MOD = n;
	for(int i=2;1;i++){
    
    
		Data[i] = (Data[i - 1] + Data[i - 2]) % MOD;
		if(Data[0] == Data[i - 1] && Data[1] == Data[i]){
    
    
			len = i - 1;
			break;
		}
	}
	int pos = fastpow(a % len, b, len);//找位置
	//考虑到a非常大,根据乘法取模的性质,将a取模,否则出错
	return Data[pos];
}
int main(){
    
    
	ull a, b;
	int t, n;
	cin >> t;
	while(t--){
    
    
		cin >> a >> b >> n;
		if(n == 1) cout << 0 <<"\n";
		//特判的原因是如果n为1,那么就会陷入死循环,因为算出来的Data值都是0
		//最好特判一下,如果不特判也可以处理,将Data[1]取为1%n
		else cout << solve(a, b, n) <<"\n";
	}
	return 0;
}
  • 因为涉及到取模的问题都会出现模完为0的情况,所以设计数组的时候直接设置Data[0],这样方便处理

猜你喜欢

转载自blog.csdn.net/roadtohacker/article/details/114463709
今日推荐