3 条题解
-
0
这是一个功能完整的 数据生成器。它包含了这道题的标准 AC 逻辑,并针对题目要求的各个数据范围(包括 、 较小、 的陷阱、以及数值溢出边界)精心设计了 10 组测试数据。
数据生成器代码 (gen.cpp)
将以下代码保存为
gen.cpp,编译运行即可在当前目录下生成1.in/out到10.in/out。#include <iostream> #include <fstream> #include <string> #include <vector> #include <cmath> #include <cstdlib> #include <ctime> using namespace std; // ========================================== // 第一部分:标准解答逻辑 (Solver) // 用于计算 .out 文件的正确答案 // ========================================== long long solve(long long a, long long b) { // 1. 特判 a=1,防止 TLE if (a == 1) return 1; // 2. 暴力循环 + 剪枝 long long ans = 1; // 题目要求的上限 const long long LIMIT = 1000000000; for (int i = 1; i <= b; i++) { // 检查溢出风险: // 如果 ans 已经大于 LIMIT,或者 ans * a 会超过 long long 范围(虽然题目限制下很难爆 ll) // 实际上题目 a, b <= 10^9,只要 ans > LIMIT 就 break 即可, // 不需要担心 long long 溢出,因为 ans 最多略大于 10^9 * 10^9 = 10^18 < 9e18 ans *= a; if (ans > LIMIT) { return -1; } } return ans; } // ========================================== // 第二部分:数据生成逻辑 // ========================================== int main() { srand(time(0)); // 初始化随机种子 cout << "正在生成 P8813 [CSP-J 2022] 乘方 测试数据..." << endl; for (int id = 1; id <= 10; id++) { string in_file = to_string(id) + ".in"; string out_file = to_string(id) + ".out"; ofstream fin(in_file); ofstream fout(out_file); long long a, b; // --- 针对不同测试点设计数据 --- if (id == 1) { // 测试点 1 (10%): b = 1, 普通情况 a = 12345; b = 1; } else if (id == 2) { // 测试点 2 (10%): b = 1, 边界情况 // 结果恰好是 10^9 a = 1000000000; b = 1; } else if (id == 3) { // 测试点 3 (30%): b <= 2, 不超限 // 30000^2 = 9*10^8 < 10^9 a = 30000; b = 2; } else if (id == 4) { // 测试点 4 (30%): b <= 2, 超限 // 40000^2 = 1.6*10^9 > 10^9 -> -1 a = 40000; b = 2; } else if (id == 5) { // 测试点 5: 特殊性质 a = 1 (TLE 陷阱) // 如果不特判,循环 10^9 次会超时 a = 1; b = 1000000000; } else if (id == 6) { // 测试点 6: 边界测试,结果恰好等于 10^9 // 10^9 = 1000000000, 应该输出原数 a = 10; b = 9; } else if (id == 7) { // 测试点 7: 边界测试,结果刚刚超过 10^9 // 2^29 < 10^9, 2^30 > 10^9 // 2^30 = 1073741824 -> -1 a = 2; b = 30; } else if (id == 8) { // 测试点 8: 极限数据 (100%) // 显然超限 -> -1 a = 1000000000; b = 1000000000; } else if (id == 9) { // 测试点 9: 随机大数 a = rand() % 1000 + 1000; // 1000~1999 b = rand() % 1000 + 1000; // 指数也很大 } else { // 测试点 10: 2 的幂次溢出 int 范围测试 // 2^31 > int_max,测试是否用了 long long a = 2; b = 32; } // --- 写入输入文件 --- fin << a << " " << b << endl; // --- 计算答案并写入输出文件 --- long long result = solve(a, b); fout << result << endl; // 关闭文件流 fin.close(); fout.close(); cout << "Generated Case " << id << ": a=" << a << ", b=" << b << " -> Ans: " << result << endl; } cout << "全部数据生成完毕!" << endl; return 0; }数据设计详解 (覆盖考点)
- Case 1-2: 针对 的情况。
- Case 1 是普通小数字。
- Case 2 是边界 ,验证程序是否能正确输出 (题目要求 才输出 -1, 应该输出原数)。
- Case 3-4: 针对 的情况。
- Case 3 结果小于 。
- Case 4 结果大于 ,测试基础判断逻辑。
- Case 5: 经典的 TLE 陷阱。
- 。如果选手的程序没有特判 且去跑循环,绝对超时。
- Case 6-7: 数值边界。
- Case 6: ,刚好压线,应输出 。
- Case 7: 亿,刚刚超过 ,测试剪枝是否准确。
- Case 10: 数据类型溢出。
- 超过了
int的最大值 ()。如果选手虽然写了剪枝,但中间变量ans用的int,这里就会计算错误(变成负数或 0),从而无法触发> 1e9的判断。这也验证了必须用long long。
- 超过了
- Case 8-9: 极限与随机大数,确保护展性。
使用这个生成器,你可以直接为 OJ 创建一套高质量的测试数据。
- Case 1-2: 针对 的情况。
-
0
这是一份标准的 AC 代码。
代码核心逻辑
- 特判 :避免 很大时循环超时。
- 暴力循环 + 剪枝:当 时,循环最多执行约 30 次就会超过 ,所以不需要快速幂,直接循环并判断即可。
- 防溢出:变量使用
long long,并在乘法后立即检查是否超过阈值。
标准 C++ 代码
#include <iostream> using namespace std; int main() { // 1. 定义变量,必须使用 long long 防止计算过程中爆 int long long a, b; cin >> a >> b; // 2. 特判 a = 1 的情况 // 如果 a 是 1,无论 b 是多少,结果都是 1 // 如果不写这一步,当 a=1, b=10^9 时循环会跑 10亿次导致 TLE if (a == 1) { cout << 1 << endl; return 0; } // 3. 循环计算 long long ans = 1; for (int i = 1; i <= b; i++) { // 累乘 ans = ans * a; // 4. 关键剪枝:每次乘完立刻检查 // 如果超过 10^9 (1e9),直接输出 -1 并结束 // 因为 a >= 2,这个循环最多执行 30 次左右就会触发 break,非常快 if (ans > 1000000000) { cout << -1 << endl; return 0; } } // 5. 如果循环正常结束,说明结果没有超过 10^9 cout << ans << endl; return 0; }常见疑问解答
- 为什么不用
pow(a, b)?pow函数返回的是double类型,当数字很大时会出现科学计数法表示,导致精度丢失(例如个位数不准)。且不易判断准确的整数溢出。 - 为什么不用快速幂?
快速幂适用于 很大且结果需要取模的情况。但这道题有 的上限截断,只要 ,循环次数极少(常数级),朴素循环配合
break反而更简单、更不容易写错。
-
0
你好!我是你的 OI 教练。
这道题(CSP-J 2022 T1)虽然是当年的签到题,但它是典型的 “坑题”,主要考察你对数据范围的敏感度和对溢出的处理。
很多同学看到求 就直接写个
for循环或者直接用pow函数,结果要么超时(TLE),要么答案错误(WA)。下面我给你提供几个关键的解题线索:
1. 最大的陷阱:超时 (TLE)
题目中 最大可以达到 (十亿)。
- 错误想法:写一个
for循环,执行 次乘法。 - 分析:如果输入
1 1000000000(即 的 次方),你的循环会跑 10 亿次,肯定会超时(通常计算机 1 秒只能跑 1 亿次左右)。 - 提示:你需要对 的情况单独写一个
if判断。如果 ,不管 是多少,答案都是 1。
2. 核心逻辑:真的需要快速幂吗?
除了 的情况,当 时,虽然 依然可能很大,但其实你不需要跑完整个循环。
- 数学直觉: 亿,已经超过题目的限制 了。
- 结论:只要 ,你的循环最多只需要执行 30 次左右,数值就会超过 。
- 做法:在循环中,每乘一次 ,立刻检查结果是否超过 。如果超过,直接输出 -1 并结束程序。这样循环次数极少,完全不用担心超时,也不需要写复杂的快速幂算法。
3. 数据类型:防爆 (
long long)题目比较的界限是 。
- 虽然 可以存进
int,但在计算过程中,你的累乘变量ans可能会瞬间变得很大。 - 比如当
ans接近 ,再乘一个 (假设 也是 ),结果会变成 。 - 风险: 远远超过了
int的范围(约 ),会导致数据溢出变成负数。 - 对策:你的计算变量
ans必须定义为long long。
4. 代码逻辑框架
你可以参考这个伪代码结构:
1. 定义 long long 类型的 a, b, ans = 1; 2. 输入 a, b 3. 【特判】:如果 a == 1,直接输出 1,结束程序。 4. 循环 i 从 1 到 b: a. ans = ans * a; b. 【关键检查】:如果 ans > 10^9 (即 1000000000): 输出 -1 结束程序 (return 0) 5. 如果循环正常结束了(说明没超),输出 ans。加油!只要处理好特判和溢出判断,这道题就很简单了。试着自己写一下吧!
- 错误想法:写一个
- 1
信息
- ID
- 12648
- 时间
- 1000ms
- 内存
- 32MiB
- 难度
- 1
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者