CCF GESP C++题解
题目一:B4002 [GESP202406 ] 平方之和
题目链接: https://www.luogu.com.cn/problem/B4002
题目描述
小杨有 \(n\) 个正整数 \(a_1, a_2, \dots, a_n\),他想知道对于所有的 \(i(1 \le i \le n)\),是否存在两个正整数 \(x\) 和 \(y\) 满足 \(x \times x + y \times y = a_i\)。如果存在输出 Yes,否则输出 No。
输入格式
- 第一行包含一个正整数 \(n\)
- 第二行包含 \(n\) 个正整数 \(a_1, a_2, \dots, a_n\)
输出格式
对于每个 \(a_i\),输出一行,如果存在满足条件的 \(x\) 和 \(y\) 输出 Yes,否则输出 No。
输入输出样例
输入 #1
5
1 2 3 4 5
输出 #1
No
Yes
No
No
Yes
说明:
- \(1\):无法表示为两个正整数的平方和
- \(2 = 1^2 + 1^2\):
Yes - \(3\):无法表示
- \(4\):无法表示(\(2^2 + 0^2\) 不满足,因为 \(y\) 必须是正整数)
- \(5 = 1^2 + 2^2\):
Yes
数据范围
- \(1 \le n \le 10\)
- \(1 \le a_i \le 10^6\)
解题思路
单层循环优化(推荐)
只枚举 \(x\),令 \(y^2 = a_i - x^2\),然后判断 \(a_i - x^2\) 是否是完全平方数。时间复杂度 \(O(n\sqrt{a_i})\)。
C++ 代码实现
#include <iostream>
#include <cmath>
using namespace std;
int main() {
int n;
cin >> n;
while (n--) {
int a;
cin >> a;
bool found = false;
// 枚举 x,x^2 必须小于 a(因为 y 至少为 1)
for (int x = 1; x * x < a; x++) {
int rest = a - x * x; // y^2 的值
int y = sqrt(rest); // 开方取整
// 检查 rest 是否是完全平方数
if (y * y == rest) {
found = true;
break;
}
}
cout << (found ? "Yes" : "No") << endl;
}
return 0;
}
关键点:
- \(x\) 的范围是 \(1\) 到 \(\sqrt{a-1}\),确保 \(y \ge 1\)
- 使用
int y = sqrt(rest)后,必须验证y * y == rest来判断是否为完全平方数 - 注意数据类型,
sqrt返回浮点数,转为int后会截断小数部分
题目二:B4007 [GESP202406 ] 计数
题目链接: https://www.luogu.com.cn/problem/B4007
题目描述
小杨认为自己的幸运数是正整数 \(k\)(\(1 \le k \le 9\))。他想知道,对于从 \(1\) 到 \(n\) 的所有正整数中,\(k\) 出现了多少次。
输入格式
- 第一行包含一个正整数 \(n\)
- 第二行包含一个正整数 \(k\)
输出格式
输出从 \(1\) 到 \(n\) 的所有正整数中,\(k\) 出现的次数。
输入输出样例
输入 #1
25
2
输出 #1
9
说明: 从 \(1\) 到 \(25\) 中,\(2\) 出现的正整数有 \(2, 12, 20, 21, 22, 23, 24, 25\),一共出现了 \(9\) 次(注意 \(22\) 中有两个 \(2\),算两次)。
数据范围
- \(1 \le n \le 1000\)
- \(1 \le k \le 9\)
解题思路
遍历 \(1\) 到 \(n\) 的每个整数,对每个整数进行拆位处理(逐位检查),统计其中等于 \(k\) 的数字个数。
拆位方法:
- 通过
num % 10获取最低位 - 通过
num / 10去掉最低位 - 循环直到数字变为 \(0\)
C++ 代码实现
#include <iostream>
using namespace std;
int main() {
int n, k;
cin >> n >> k;
int count = 0; // 统计出现次数
for (int i = 1; i <= n; i++) {
int num = i; // 使用临时变量保存 i,避免修改循环变量
// 拆位:逐位检查数字
while (num != 0) {
int digit = num % 10; // 取出最低位
if (digit == k) {
count++; // 找到一位 k,计数器加 1
}
num /= 10; // 去掉最低位
}
}
cout << count << endl;
return 0;
}
关键点:
- 必须使用临时变量
num = i,不能直接对i进行操作,否则会影响外层循环 - 注意是统计每一位出现的次数,不是统计包含 \(k\) 的数字个数(例如 \(22\) 算两次)
- 数据范围很小(\(n \le 1000\)),直接枚举即可
总结对比
| 题目 | 题目链接 | 核心考点 | 算法思路 | 时间复杂度 |
|---|---|---|---|---|
| B4002 平方之和 | https://www.luogu.com.cn/problem/B4002 | 循环嵌套、数学判断 | 枚举 + 完全平方数判断 | \(O(n\sqrt{a})\) |
| B4007 计数 | https://www.luogu.com.cn/problem/B4007 | 拆位技巧、循环遍历 | 枚举 + 逐位分解 | \(O(n \times \text{位数})\) |

浙公网安备 33010602011771号