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;
}

关键点:

  1. \(x\) 的范围是 \(1\)\(\sqrt{a-1}\),确保 \(y \ge 1\)
  2. 使用 int y = sqrt(rest) 后,必须验证 y * y == rest 来判断是否为完全平方数
  3. 注意数据类型,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;
}

关键点:

  1. 必须使用临时变量 num = i,不能直接对 i 进行操作,否则会影响外层循环
  2. 注意是统计每一位出现的次数,不是统计包含 \(k\) 的数字个数(例如 \(22\) 算两次)
  3. 数据范围很小(\(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{位数})\)
posted @ 2026-03-12 18:49  kkman2000  阅读(4)  评论(0)    收藏  举报