递推式的循环问题

 

 

形如 斐波拉契数列 这样的形形色色的递推式 模上一个数M过后的值 是一个循环的效果。

可以这样解释 拿斐波拉契数列举例 F[i] = F[i - 1] + F[i - 2] ;F[i] 如果模上一个数M可以得到M个结果, 并且是由前两项转移过来的, 那么最多有 M ^ 2 项会出现循环,但往往是小于M ^ 2 的,如果是由前三项转移的话,那么最多经过M ^ 3项后出现循环

 

 

套路一:

数据允许的范围求循环节

给出两个非整数a, b, n(0  <= a, b <= 2^64, 1 <= n <= 1000),任务是计算出f(a^b) % n, 其中f[0] = f[1] = 1, f[i] = f[i - 1] + f[i - 2]

 

由上面知道最多会有 n ^ 2 项会出现循环,判断出现循环的方法:连续出现 (f[0] : 1, f[1] : 1),因为循环的实质是前两项出现循环,那么方法就很明显了,首先找到关于数列模上n的循环节长度,然后用快速幂计算a ^ b % n,然后就知道a ^ b在一个循环节的哪一项了。

 

代码:

#include <bits/stdc++.h>
using namespace std;

#define ll unsigned long long

const int N = 1e6 + 7;
ll a, b, n;
int kase, F[N], M;

ll readll () {
	ll K = 0; char c = getchar();
	while (c < '0' || c > '9') c = getchar();
	while (c >= '0' && c <= '9') K = K * 10 + c - '0',c = getchar();
	return K;
}

int pow (ll x,ll y){
	ll ret = 1;
	while (y) {
		if(y & 1) ret = (x * ret) % M;
		x = (x % M) * (x % M) % M;
		y >>= 1;
	}
	return ret;
}

int main(){
	scanf ("%d", &kase);
	while (kase--) {
		a = readll(), b = readll(), n = readll();
		if( !a || n==1 ){puts("0");continue;}
		F[0] = F[1] = 1;
		int cur = 2,flag = 0;
		while (true) {
			F[cur] = (F[cur-1] + F[cur-2]) % n;
			if (F[cur] != 1) flag = 0;
			else if (F[cur] == 1 && flag == 0) flag = 1;
			else if (F[cur] == 1 && flag == 1) break;
			++cur;
		}
		M = cur - 1;
		printf("%d\n", F[(pow(a, b) - 1 + M) % M]);
	}
	return 0;
}

  

套路二

对于特定的模打表找循环节

依然拿斐波拉契数列举例,给出一个数字n (<=10^1000),求f[f[i]] % 1e9+7 的数值

对于f[x] % M 项的数值最坏情况下会出现M ^ 2的循环,但是肯定要找循环节,所以打一个表碰运气看他的循环是多少,根据实践循环节的长度为2e9 + 16,那么在里面的f[i]就模上2e9 + 16,现在转换为f[x] % 2e9 + 16的问题了,一样的打表找循环节,长度为329616,那么n就可以直接模上329616,剩下的就是矩阵的事情了~

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define ll long long
char ch[1050];
int mod[3];
struct Matrix {
	ll map[3][3];
	void clear () {memset(map, 0, sizeof (map));}
};

Matrix Mul (Matrix x, Matrix y, int F) {
	Matrix ret;
	ret.clear();
	for (int i = 0; i < 2; ++i) 
		for (int j = 0; j < 2; ++j)
			for (int k = 0; k < 2; ++k)
				ret.map[i][j] = (ret.map[i][j] + x.map[i][k] * y.map[k][j] % mod[F]) % mod[F];
	return ret;
}

Matrix Pow (Matrix x, int n, int F) {
	Matrix ret;
	ret.clear();
	ret.map[0][0] = 1;
	ret.map[1][1] = 1;
	while (n) {
		if (n & 1) ret = Mul(ret, x, F);
		x = Mul(x, x, F);
		n >>= 1;
	}
	return ret;
}

int main () {
	int kase;
	mod[1] = 2e9 + 16, mod[2] = 1e9 + 7, mod[3] = 329616;
	scanf ("%d", &kase); 
	while (kase--) {
		ll n = 0;
		scanf ("%s", &ch);
		int len = strlen(ch);
		for (int i = 0; i < len; ++i) n = (n * 10 % 329616 + ch[i] - '0') % 329616;
		Matrix ans, ori;
		ans.clear(), ori.clear();
		ans.map[0][0] = 0;
		ans.map[0][1] = 1;
		ori.map[0][0] = 0;
		ori.map[0][1] = 1;
		ori.map[1][0] = 1;
		ori.map[1][1] = 1;
		ans = Mul(ans, Pow(ori, n, 1), 1);
		n = ans.map[0][0];
		ans.map[0][0] = 0;
		ans.map[0][1] = 1;
		ans = Mul(ans, Pow(ori, n, 2), 2);
		printf ("%d\n", ans.map[0][0]);
	}
	return 0;
}

  

 

posted @ 2016-10-15 16:33  xgtao  阅读(502)  评论(0编辑  收藏  举报