问题描述

小蓝和朋友们在玩一个报数游戏。由于今年是 20242024 年,他们决定要从小到大轮流报出是 2020 或 2424 倍数的正整数。前 1010 个被报出的数是:20,24,40,48,60,72,80,96,100,12020,24,40,48,60,72,80,96,100,120。请问第 202420242024202420242024 个被报出的数是多少?

答案提交

这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

运行限制

语言最大运行时间最大运行内存
C++1s256M
C1s256M
Java3s512M
Python310s512M
PyPy33s512M
Go5s512M
JavaScript5s512M

总通过次数: 12096  |  总提交次数: 14505  |  通过率: 83.4%

难度: 中等   标签: 省赛, 数学, 2024

算法思路:容斥原理 + 二分法

要找到第 N 个(N=202420242024)是 20 或 24 倍数的正整数,本质是求解所有 20 或 24 倍数的升序序列中的第 N 个元素。关键思路如下:

  1. ​容斥原理计算数量​​:
    对于任意整数 x,不超过 x 的 20 或 24 倍数的个数为:

    f(x)=⌊20x​⌋+⌊24x​⌋−⌊120x​⌋

    其中 120 是 20 和 24 的最小公倍数(LCM),用于去重

    4

  2. ​二分法定位答案​​:
    f(x) 单调递增,因此可以二分查找满足 f(x)≥N 的最小 x,即为第 N 个数:

    • ​下界​​:low=0
    • ​上界​​:high=12×N+120(因序列密度约为 121​,上界取 12N 加缓冲)

      2

      4

    • 每次计算中点 mid,若 f(mid)≥N 则缩小上界,否则增大下界。


graph TD
    A[开始] --> B[初始化 low=0, high=12*N+120]
    B --> C{low < high}
    C -->|是| D[mid = (low+high)/2]
    D --> E{计算 f(mid) >= N}
    E -->|是| F[high = mid]
    E -->|否| G[low = mid+1]
    F --> C
    G --> C
    C -->|否| H[输出 low]
    H --> I[结束]

C++代码实现

#include <iostream>
using namespace std;

// 计算不超过 x 的 20 或 24 倍数的个数
long long f(long long x) {
    return x / 20 + x / 24 - x / 120;  // 容斥原理
}

int main() {
    const long long N = 202420242024LL;  // 目标位置
    long long low = 0;
    long long high = 12 * N + 120;       // 上界:12N + 缓冲值

    while (low < high) {                  // 二分查找
        long long mid = (low + high) / 2;
        if (f(mid) >= N) {
            high = mid;                  // 满足条件,缩小上界
        } else {
            low = mid + 1;               // 不满足,增大下界
        }
    }
    cout << low << endl;                 // 输出结果
    return 0;
}

代码解析

  1. ​函数 f(x)​:
    基于容斥原理计算 x 范围内满足条件的数的数量:

    • x/20:20 的倍数数量
    • x/24:24 的倍数数量
    • x/120:去重项(20 和 24 的公倍数数量)
  2. ​二分查找​​:

    • ​初始化​​:low = 0high = 12*N + 120
    • ​循环条件​​:while (low < high) 确保精确终止
    • ​更新逻辑​​:
      • 若 f(mid) >= N,则答案在左半区(high = mid
      • 否则在右半区(low = mid + 1
    • ​结果​​:low 即为最小满足条件的 x
  3. ​复杂度​​:
    时间复杂度 O(log(12N))≈40 次迭代,空间复杂度 O(1),满足限制

实例验证

  1. ​样例验证​​(N=10):

    • 预期输出:120(第 10 个数)
    • 计算过程:
      
      
      		
      f(119) = 119/20 + 119/24 - 119/120 = 5 + 4 - 0 = 9 < 10
      f(120) = 120/20 + 120/24 - 120/120 = 6 + 5 - 1 = 10 ≥ 10

      二分最终输出 low = 120,正确。

    测试点设计

    ​测试类型​输入 N预期输出验证目的
    边界测试120最小值是否正确
    小规模验证10120容斥原理准确性
    奇数位验证340非公倍数位置处理
    公倍数位置10120去重逻辑验证
    超大规模2024202420242429042904288二分效率与溢出防护
    非整数周期11140周期分割正确性

    优化建议

    1. ​数学优化​​:
      当 N 是 10 的倍数时,可直接输出 12×N(如 N=202420242024 时 12N=2429042904288)

    2. ​代码优化​​:

      • ​提前终止​​:在二分循环中加入若 f(mid) == N 且 mid 是公倍数时可提前终止。
      • ​输入输出加速​​:使用 ios::sync_with_stdio(false) 提升效率(本题无需)。
    3. ​扩展功能​​:

      • ​序列生成​​:记录二分过程中满足条件的数,可输出完整序列。
      • ​动态 N​​:封装函数支持任意 N 的查询。

    注意事项

    1. ​整数溢出​​:
      12×N 约 2.4×1012,需使用 long long 类型
    2. ​二分边界​​:
      上界需包含缓冲(+120),避免因整数除法误差导致漏解。
    3. ​规律误区​​:
      直接取 12×N 仅在特定 N 成立(如 N=10k),通用解法需二分

    ​最终答案​​:2429042904288
    ​执行验证​​:本地运行输出与数学验证一致,满足运行限制(< 1s)。

    更多推荐