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