皮萨诺周期
斐波那契数列对 m 取余后,得到的余数序列一定会出现循环,这个循环的长度就叫皮萨诺周期。皮萨诺周期长度 ≤ 6m
用法:
-
防止死循环,快速判定 “是否存在解”
:找 fib(i) mod m = a 的最小 i
若在一个周期内没出现,则永远不会出现
周期长度 ≤ 6m,直接循环 6m 次即可,不用无限跑 -
超大项斐波那契取模
求 fib(n) mod m,其中 n 极大(1e18、1e100000)
直接算不可能,用周期:
求出 m 的皮萨诺周期 p
等价求 fib(n mod p) mod m -
循环节相关计数 / 统计
统计 0~m-1 每个余数出现多少次
求前 N 项中余数为 x 的项数
求前 N 项余数和
思路:
先算一个周期内的结果 → 乘以周期数 → 处理剩余部分 -
快速判断重复状态、优化暴力
斐波那契递推类 DP、模运算题:
连续两个余数只有 m*m 种组合
一旦重复就开始循环
可以用来剪枝、提前退出、找规律
板子:
include <bits/stdc++.h>
using namespace std;
using ll = long long;
// 1. 求皮萨诺周期
ll pisano(ll m) {
if (m == 1) return 1;
ll a = 1, b = 1, c;
ll period = 0;
while (true) {
c = (a + b) % m;
a = b;
b = c;
period++;
// 回到初始 (1,1) 则循环结束
if (a == 1 && b == 1) break;
}
return period;
}
//2. 快速求 fib(n) mod m(n可到1e18)
ll fib_mod(ll n, ll m) {
if (n <= 2) return 1 % m;
ll p = pisano(m); // 先求周期
n = n % p; // 缩小n
if (n == 0) n = p; // 余0等价周期末项
ll a = 1, b = 1, c;
for (ll i = 3; i <= n; ++i) {
c = (a + b) % m;
a = b;
b = c;
}
return b;
}
// 3.找第一个 fib(i)%m ==a 的天数
ll find_first_day(ll m, ll a) {
ll f1 = 1, f2 = 1;
// 第1天
if (f1 % m == a) return 1;
// 第2天
if (f2 % m == a) return 2;
ll max_step = 6 * m; // 安全上限
for (ll day = 3; day <= max_step; ++day) {
ll f3 = (f1 + f2) % m;
if (f3 == a) return day;
f1 = f2;
f2 = f3;
}
return -1; // 不存在
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
ll m, a;
cin >> m >> a;
cout << find_first_day(m, a) << endl;
return 0;
}

浙公网安备 33010602011771号