形如 斐波拉契数列 这样的形形色色的递推式 模上一个数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;
}