C++ 算法考级专栏
真题分析、矩阵探测、递归回溯与基础语法
🎨 视觉封面【GESP】C++五级真题 luogu-P15798, [GESP202603 五级] 有限不循环小数
2026年3月,GESP五级真题,考察数论基础(质因数分解特性)枚举算法,难度⭐⭐★☆☆。洛谷难度级别:普及-。
P15798 [GESP202603 五级] 有限不循环小数
题目要求
题目描述
若 可化为一个有限的,不循环的小数,则称 为终止数。 请你求出在 到 中终止数的数量。
输入格式
输入一行,包含两个整数 。
输出格式
输出一行,包含一个整数,表示 到 中终止数的数量。
输入输出样例 #1
输入 #1
2 11
输出 #1
5
说明/提示
样例解释
在 中,终止数有 、、、、。
数据范围
保证 。
题目分析
这道题目虽然披着"小数"的外衣,本质上是一道数论题。在 GESP 五级考试中,数论基础(如质数判断、质因数分解、最大公约数等)是高频重点。
数学原理破解:什么样的分数能化成有限且不循环的小数?
我们在小学数学中学过:一个最简分数能不能化成有限小数,完全取决于它的分母 。由于我们使用的数学计数法是十进制(逢十进一),而 。
逻辑推导是这样的:
假设 可以化为有限小数,这意味着它一定可以写成 的形式(其中 和 都是正整数)。 由 ,我们可以通过交叉相乘推导出 。 因为 ,即 这个数字的全部质因数只有 和 。 既然 是 的一个约数(因为 乘以整数 等于 ),那么 身上包含的质因数必定也只能是 或 ,绝不可能含有 等其它任何质数。
因此,当且仅当一个数 的质因数只包含 和 时, 才能被除尽,化为有限且不循环的小数。 也就是说,所谓"终止数" ,一定满足特定的数学构造形式:(其中 和 是非负整数,即 ),并且 必须在题目要求的范围之内。
算法思路:如何找到所有终止数?
有了上面的数学结论,接下来就是怎么在 的范围内找到所有满足条件的数。这里提供两种思路:
思路一:逐个检验法(直接分解)
最直观的想法:从 到 ,每个数都拿过来"查户口"。对于当前数 ,不停地除以 (只要能整除就除),再不停地除以 (只要能整除就除),把 和 的因子全部剥干净。如果最后剩下的数等于 ,说明 的质因数只有 和 ,它就是"终止数";否则说明它还含有其他质因数,不合格。
这个方法逻辑简单清晰,容易理解和编写。时间复杂度为 ,在 的数据范围内完全够用。
思路二:主动生成法(枚举幂次)
换一个角度——与其挨个排查,不如直接按照公式把所有终止数"造"出来。
既然终止数的形式是 ,我们只需要用两层循环枚举 和 的值:
- 的幂次 :因为 ,所以 从 枚举到 就足够了。
- 的幂次 :因为 ,,所以 从 枚举到 就足够了。
这样总共只有大约 次判定,效率极高。最后判断生成的 是否落在 内即可。
示例代码
思路一:逐个检验法(直接分解)
#include <iostream>
// 判断一个数是否为"终止数"
// 核心逻辑:不断除以 2 和 5,看最终能否除尽变成 1
bool isTerminating(int a) {
// 先把所有的因子 2 除干净
while (a % 2 == 0) {
a /= 2;
}
// 再把所有的因子 5 除干净
while (a % 5 == 0) {
a /= 5;
}
// 如果剩下的是 1,说明 a 的质因数只有 2 和 5
return a == 1;
}
int main() {
int L, R;
std::cin >> L >> R;
int ans = 0; // 用于统计符合要求的"终止数"的个数
// 从 L 到 R,逐个检验每个数是否为终止数
for (int i = L; i <= R; i++) {
if (isTerminating(i)) {
ans++;
}
}
// 输出最终结果
std::cout << ans << std::endl;
return 0;
}
思路二:主动生成法(枚举幂次)
#include <iostream>
int main() {
long long L, R;
std::cin >> L >> R;
int ans = 0; // 用于统计符合要求的"终止数"的个数
// 外层循环枚举 2 的幂次,变量 p2 用来存放 2^x 的值
// p2 不断乘以 2,直到超出 R 的最大范围
for (long long p2 = 1; p2 <= R; p2 *= 2) {
// 内层循环枚举 5 的幂次,变量 p5 用来存放 5^y 的值
// p5 不断乘以 5,同时我们要确保 p2 * p5 的总积不能超过 R
for (long long p5 = 1; p2 * p5 <= R; p5 *= 5) {
// 组装出当前的 a 值
long long a = p2 * p5;
// 如果这个生成的 a 值刚好落在 [L, R] 范围内,计数加一
if (a >= L && a <= R) {
ans++;
}
}
}
// 最终输出满足条件的数量
std::cout << ans << std::endl;
return 0;
}
代码解析小贴士
- 思路一的核心技巧——"剥洋葱": 判断一个数的质因数是否只包含 和 ,不需要做完整的质因数分解。只要用
while循环反复除以 、再反复除以 ,最后看结果是不是 。如果是 ,说明"洋葱"已经被剥光了,质因数确实只有 和 。这个技巧在数论题中非常实用。 - 思路二的效率优势——"查户口"不如"自己生": 当在一个大范围里寻找具备极少特征的特殊数字时,从头到尾挨个排查虽然可行但比较笨拙。把特殊的数字按照生成规则反向拼装创造出来,往往能将时间复杂度从 降到 。
- 乘风破浪的
long long: 在思路二中,尽管题目给定 还在int承受范围内,但是在写底数倍增循环(如p2 *= 2,p5 *= 5)时,下一次循环乘后的预判值极容易越界爆出int的上限导致死循环或错误。在处理所有涉及连乘倍增的题目时,养成使用long long的好习惯。
所有代码开源上传至 GitHub:yummy-code 仓库 · GESP 专题站:GESP WIKI
欢迎加入:C++ GESP/CSP 考级答疑群(688906745) 与 Java/Python交流群(982860385),点击可直接加群。
猜你想读 · 相关文章推荐
【GESP】C++五级练习(初等数论考点) luogu-B3941 [GESP样题 五级] 小杨的锻炼
GESP C++ 五级初等数论考点练习,难度⭐⭐★☆☆。洛谷难度等级普及−
【GESP】C++五级(四级也可)练习(四级排序考点) luogu-B3951 [GESP样题 五级] 小杨的队列
GESP C++ 五级练习,但是核心的考点是四级排序内容,因为是GESP五级样例,所以归类在五级练习之中,其实四级同学完全可以练习,难度⭐⭐★☆☆。洛谷难度等级普及−
【GESP】C++五级真题(数论考点) luogu-B3871 [GESP202309 五级] 因数分解
GESP C++ 2023年9月五级真题,数论考点,难度⭐⭐★☆☆。洛谷难度等级普及−
OneCoder (lihongzheshuai)
一个中年人的自留地,记录学习 C++、GESP/NOI、Java、Python 与算法架构的心得体会。本站唯一网址:coderli.com