1 条题解
-
0
「EGOI2021」零的个数 题解
题目分析
我们需要找到区间 内所有整数的最小公倍数 ,然后计算 末尾有多少个零。
关键观察:一个数末尾零的个数等于其质因数分解中 和 的指数的最小值。
数学推导
1. 末尾零的本质
设 ,其中 不被 或 整除,那么末尾零的个数为 。
2. 最小公倍数的质因数分解
对于质数 ,在 中 的指数等于区间 中 的最高幂次。
即:
$$\text{exp}_p(L) = \max\{k : \exists x \in [a, b] \text{ 使得 } p^k \mid x\} $$3. 问题转化
我们需要计算:
- $\alpha = \max\{k : \exists x \in [a, b] \text{ 使得 } 2^k \mid x\}$
- $\beta = \max\{k : \exists x \in [a, b] \text{ 使得 } 5^k \mid x\}$
答案就是 。
算法思路
方法一:直接枚举(适用于小数据)
对于 的情况,可以实际计算 LCM:
#include <iostream> #include <algorithm> using namespace std; long long gcd(long long a, long long b) { return b == 0 ? a : gcd(b, a % b); } long long lcm(long long a, long long b) { return a / gcd(a, b) * b; } int countZeros(long long n) { int cnt = 0; while (n % 10 == 0) { cnt++; n /= 10; } return cnt; } int main() { long long a, b; cin >> a >> b; long long L = a; for (long long i = a + 1; i <= b; i++) { L = lcm(L, i); } cout << countZeros(L) << endl; return 0; }方法二:数学分析(适用于大数据)
对于 ,我们需要高效的数学方法:
#include <iostream> #include <algorithm> using namespace std; // 计算在区间[a, b]中,p^k的倍数是否存在 bool exists_power(long long a, long long b, long long p, int k) { long long power = 1; for (int i = 0; i < k; i++) { if (power > b / p) return false; // 防止溢出 power *= p; } // 检查区间内是否存在power的倍数 long long first_multiple = ((a + power - 1) / power) * power; return first_multiple <= b; } int main() { long long a, b; cin >> a >> b; // 计算2的最高幂次 int max_power_2 = 0; for (int k = 1; ; k++) { if (!exists_power(a, b, 2, k)) { max_power_2 = k - 1; break; } } // 计算5的最高幂次 int max_power_5 = 0; for (int k = 1; ; k++) { if (!exists_power(a, b, 5, k)) { max_power_5 = k - 1; break; } } cout << min(max_power_2, max_power_5) << endl; return 0; }优化版本
由于 和 增长很快,循环次数不会太多:
#include <iostream> #include <algorithm> using namespace std; int count_trailing_zeros(long long a, long long b) { int count_2 = 0, count_5 = 0; // 统计因子2的最高幂次 long long power_2 = 1; for (int k = 1; ; k++) { if (power_2 > b / 2) break; // 防止溢出 power_2 *= 2; // 检查区间内是否存在power_2的倍数 long long first = ((a + power_2 - 1) / power_2) * power_2; if (first <= b) { count_2 = k; } else { break; } } // 统计因子5的最高幂次 long long power_5 = 1; for (int k = 1; ; k++) { if (power_5 > b / 5) break; // 防止溢出 power_5 *= 5; // 检查区间内是否存在power_5的倍数 long long first = ((a + power_5 - 1) / power_5) * power_5; if (first <= b) { count_5 = k; } else { break; } } return min(count_2, count_5); } int main() { long long a, b; cin >> a >> b; cout << count_trailing_zeros(a, b) << endl; return 0; }样例分析
样例1:
1 6- 检查 : (存在), (存在), (不存在) → count_2 = 2
- 检查 : (存在), (不存在) → count_5 = 1
- 答案:min(2, 1) = 1
样例2:
10 11- 检查 : (存在), (存在), (存在), (不存在) → count_2 = 3
- 检查 : (存在), (不存在) → count_5 = 1
- 答案:min(3, 1) = 1
算法正确性
- 完备性:检查了所有可能的 和
- 正确性:基于最小公倍数的质因数分解性质
- 高效性:循环次数为
复杂度分析
- 时间复杂度:,因为 和 指数增长
- 空间复杂度:
- 支持范围:
总结
这道题的关键在于:
- 理解末尾零与质因子 、 的关系
- 掌握最小公倍数的质因数分解性质
- 使用数学方法避免直接计算大数
通过检查区间内是否存在 和 的倍数,我们可以高效地解决问题,即使对于 的大数据范围。
- 1
信息
- ID
- 4057
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 3
- 已通过
- 1
- 上传者