Biorhythms 生理周期

GitHub跳转原题关系图返回列表

把三个周期高峰同天写成同余方程组,用中国剩余定理(CRT)求出周期 23×28×33=21252 内的唯一解,再对给定时间做环形减法得到下一次同天的距离。

OJ: roj

题目 ID: 1639

难度:普及

标签:数论中国剩余定理同余方程

创建: 2026-09-30 23:31

更新: 2026-10-06 01:36

题目描述

人生三体力、情商、智商的高峰周期分别为 23、28、33 天,每个周期从 0 天起算并在周期长度处同时达到高峰。给定三个高峰出现的日子与目标日子 dd(均为出生后天数),求下一个三高峰同日距出生多少天(−1-1 为终止)。

思路

三高峰同日即解同余方程组 x≡p(mod23/28/33)x \equiv p \pmod{23/28/33},由中国剩余定理得模 M=23×28×33M=23\times28\times33 的唯一解,再加减 MM 调整到 dd 之后的最小正解(结果 ≤M\le M 保证在 int 范围内)。

参考代码

Python 版:

python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-30 23:31
# update_at: 2026-09-30 23:31

import sys
from itertools import count

MOD = [23, 28, 33]      # 体力、情感、智力三个周期的长度
M = 23 * 28 * 33        # 21252:三个周期的公共周期,答案必落在其中


def triple_peak(rem: list[int]) -> int:
    """求 x ≡ rem[k] (mod MOD[k]) 的最小非负解(中国剩余定理)。"""
    return sum(
        r * (M // m) * pow(M // m, -1, m)  # M//m 与其逆的乘积只在 mod m 处取 1,其余模数处取 0
        for m, r in zip(MOD, rem)
    ) % M


def solve() -> None:
    data = iter(map(int, sys.stdin.buffer.read().split()))
    out: list[str] = []

    for case in count(1):
        p, e, i, d = next(data), next(data), next(data), next(data)
        if p == e == i == d == -1:  # 输入结束哨兵
            break

        # 同天时刻 x 满足三个同余;下一次严格晚于 d 的同天时刻相距 (x - d) mod M,
        # 恰好落在 d 上时按题意跳过,答案取整个周期 M
        days = (triple_peak([p, e, i]) - d - 1) % M + 1
        out.append(f"Case {case}: the next triple peak occurs in {days} days.")

    print('\n'.join(out))


if __name__ == "__main__":
    solve()

C++ 版(同一算法):

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-10-06 01:29
 * update_at: 2026-10-06 01:29
 */
#include <cstdio>

typedef long long ll;

const ll M = 23 * 28 * 33; // 21252:三个周期的公共周期,答案必在其中
const ll MI[3] = {924, 759, 644};  // B[k] = M / m[k]
const ll TI[3] = {6, 19, 2};       // t[k] = B[k] 的逆元 mod m[k],即 B[k]*t[k] 只在 mod m[k] 处余 1

// 用中国剩余定理求 x ≡ r[k] (mod m[k]) 的最小非负解,m 分别为 23, 28, 33
ll crt(ll r1, ll r2, ll r3) {
    ll base[3] = {r1, r2, r3};
    ll x = 0;
    for (int k = 0; k < 3; k++)
        x += base[k] % (M / MI[k]) * MI[k] * TI[k];
    return x % M;
}

int main() {
    ll p, e, i, d;
    int cas = 0;
    while (scanf("%lld %lld %lld %lld", &p, &e, &i, &d) == 4) {
        if (p == -1 && e == -1 && i == -1 && d == -1)
            break;

        // 三个高峰同天时刻 x 在 [0, M) 内唯一;下一次严格晚于 d 的距离用
        // (x - d - 1) % M + 1 统一处理:x <= d 时自动跳到下一圈,
        // x == d 时按题意跳过当天、答案为整个周期 M
        ll x = crt(p, e, i);
        // 注意 C++ 负数取模结果可能为负,先 +M 再取模
        ll ans = ((x - d - 1) % M + M) % M + 1;
        printf("Case %d: the next triple peak occurs in %lld days.\n", ++cas, ans);
    }
    return 0;
}