皮萨诺周期

斐波那契数列对 m 取余后,得到的余数序列一定会出现循环,这个循环的长度就叫皮萨诺周期。皮萨诺周期长度 ≤ 6m
用法:

  1. 防止死循环,快速判定 “是否存在解”
    :找 fib(i) mod m = a 的最小 i
    若在一个周期内没出现,则永远不会出现
    周期长度 ≤ 6m,直接循环 6m 次即可,不用无限跑

  2. 超大项斐波那契取模
    求 fib(n) mod m,其中 n 极大(1e18、1e100000)
    直接算不可能,用周期:
    求出 m 的皮萨诺周期 p
    等价求 fib(n mod p) mod m

  3. 循环节相关计数 / 统计
    统计 0~m-1 每个余数出现多少次
    求前 N 项中余数为 x 的项数
    求前 N 项余数和
    思路:
    先算一个周期内的结果 → 乘以周期数 → 处理剩余部分

  4. 快速判断重复状态、优化暴力
    斐波那契递推类 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;

}

posted @ 2026-04-08 21:43  lagranSun  阅读(10)  评论(0)    收藏  举报