[蓝桥杯]报数游戏
问题描述
小蓝和朋友们在玩一个报数游戏。由于今年是 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++ | 1s | 256M |
| C | 1s | 256M |
| Java | 3s | 512M |
| Python3 | 10s | 512M |
| PyPy3 | 3s | 512M |
| Go | 5s | 512M |
| JavaScript | 5s | 512M |
总通过次数: 12096 | 总提交次数: 14505 | 通过率: 83.4%
难度: 中等 标签: 省赛, 数学, 2024
算法思路:容斥原理 + 二分法
要找到第 N 个(N=202420242024)是 20 或 24 倍数的正整数,本质是求解所有 20 或 24 倍数的升序序列中的第 N 个元素。关键思路如下:
-
容斥原理计算数量:
f(x)=⌊20x⌋+⌊24x⌋−⌊120x⌋
对于任意整数 x,不超过 x 的 20 或 24 倍数的个数为:其中 120 是 20 和 24 的最小公倍数(LCM),用于去重
4
。 -
二分法定位答案:
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;
}
代码解析
-
函数
f(x):
基于容斥原理计算 x 范围内满足条件的数的数量:x/20:20 的倍数数量x/24:24 的倍数数量x/120:去重项(20 和 24 的公倍数数量)
-
二分查找:
- 初始化:
low = 0,high = 12*N + 120 - 循环条件:
while (low < high)确保精确终止 - 更新逻辑:
- 若
f(mid) >= N,则答案在左半区(high = mid) - 否则在右半区(
low = mid + 1)
- 若
- 结果:
low即为最小满足条件的 x
- 初始化:
-
复杂度:
时间复杂度 O(log(12N))≈40 次迭代,空间复杂度 O(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 ≥ 10low = 120,正确。
-
测试点设计
| 测试类型 | 输入 N | 预期输出 | 验证目的 |
|---|---|---|---|
| 边界测试 | 1 | 20 | 最小值是否正确 |
| 小规模验证 | 10 | 120 | 容斥原理准确性 |
| 奇数位验证 | 3 | 40 | 非公倍数位置处理 |
| 公倍数位置 | 10 | 120 | 去重逻辑验证 |
| 超大规模 | 202420242024 | 2429042904288 | 二分效率与溢出防护 |
| 非整数周期 | 11 | 140 | 周期分割正确性 |
优化建议
-
数学优化:
当 N 是 10 的倍数时,可直接输出 12×N(如 N=202420242024 时 12N=2429042904288) -
代码优化:
- 提前终止:在二分循环中加入若
f(mid) == N且mid是公倍数时可提前终止。 - 输入输出加速:使用
ios::sync_with_stdio(false)提升效率(本题无需)。
- 提前终止:在二分循环中加入若
-
扩展功能:
- 序列生成:记录二分过程中满足条件的数,可输出完整序列。
- 动态 N:封装函数支持任意 N 的查询。
注意事项
- 整数溢出:
12×N 约 2.4×1012,需使用long long类型 - 二分边界:
上界需包含缓冲(+120),避免因整数除法误差导致漏解。 - 规律误区:
直接取 12×N 仅在特定 N 成立(如 N=10k),通用解法需二分
最终答案:2429042904288
执行验证:本地运行输出与数学验证一致,满足运行限制(< 1s)。
更多推荐
所有评论(0)