OneCoder Avatar
OneCodercoderli.com · 960 篇博文
五级

C++ 算法考级专栏

真题分析、矩阵探测、递归回溯与基础语法

🎨 视觉封面

【GESP】C++五级真题(数论-素数思想考点) luogu-P10720 [GESP202406 五级] 小杨的幸运数字

📅 2025-11-03·✍️ OneCoder·计算中...·⏱️ 9 分钟
#GESP#C++#数论

GESP C++ 2024年6月五级真题,数论-素数思想考点,难度⭐⭐★☆☆,属于五级真题中比较简单的。洛谷难度等级普及−

luogu-P10720 [GESP202406 五级] 小杨的幸运数字

题目要求

题目描述

小杨认为他的幸运数字应该恰好有两种不同的质因子,例如,12=2×2×312=2\times 2\times 3 的质因子有 2,32,3,恰好为两种不同的质因子,因此 1212 是幸运数字,而 30=2×3×530=2\times3\times5 的质因子有 2,3,52,3,5,不符合要求,不为幸运数字。

小杨现在有 nn 个正整数,他想知道每个正整数是否是他的幸运数字。

输入格式

第一行包含一个正整数 nn,代表正整数个数。

之后 nn 行,每行一个正整数。

输出格式

输出 nn 行,对于每个正整数,如果是幸运数字,输出 11,否则输出 00

输入输出样例 #1

输入 #1
PLAINTEXT 4 行
3
7
12
30
输出 #1
PLAINTEXT 3 行
0
1
0

说明/提示

样例解释

77 的质因子有 77,只有一种。

1212 的质因子有 2,32,3,恰好有两种。

3030 的质因子有 2,3,52,3,5,有三种。

数据范围
子任务编号 数据点占比 nn 正整数值域
11 40%40\% 100\leq 100 105\leq 10^5
22 60%60\% 104\leq 10^4 106\leq 10^6

对于全部数据,保证有 1n1041\leq n\leq 10^4,每个正整数 aia_i 满足 2ai1062\leq a_i\leq 10^6


题目分析

解题思路

  1. 读入 nnnn 个正整数。
  2. 对每个数 aa
    • 初始化计数器 count = 0
    • 22 开始试除到 a\lfloor\sqrt{a}\rfloor
      – 若 jaj\mid a,说明 jj 是一个质因子,count++
      – 把 aa 中所有 jj 因子全部除掉,避免重复。
      – 一旦 count > 2 立即 break,提前剪枝。
    • 若最终剩余 a>1a>1,则剩余部分本身也是质因子,count++
  3. 判断 count == 2 输出 11,否则输出 00

复杂度
单次分解最坏 O(ai)O(\sqrt{a_i})n104, ai106n\le 10^4,\ a_i\le 10^6 时总运算量约 104×103=10710^4\times 10^3=10^7 级,可轻松通过。
空间仅若干变量,O(1)O(1)

该做法代码极短,无需预处理,在五级数据范围内已足够高效;若值域再大,可改为“埃氏筛+质数表试除”进一步加速(不做展开,详见示例代码)。


示例代码

方法一、直接法

C++ 35 行
#include <iostream>
#include <cmath>

int main() {
    int n;
    std::cin >> n;                       // 读入待检测的正整数个数

    for (int i = 0; i < n; i++) {
        int a;
        std::cin >> a;                   // 读入当前待检测的正整数
        int count = 0;                   // 记录不同质因子的个数
        // 试除法枚举可能的质因子,只需到 sqrt(a)
        for (int j = 2; j <= sqrt(a); j++) {
            if (a % j == 0) {            // j 是 a 的一个质因子
                count++;                 // 发现新的质因子
            }
            // 将 a 中所有 j 因子全部除掉,避免重复计数
            while (a % j == 0) {
                a /= j;
            }
            // 提前退出:已经超过两种质因子,必定不是幸运数字
            if (count > 2) {
                break;
            }
        }
        // 若剩余 a>1,则剩下的 a 本身也是一个质因子
        if (a > 1) {
            count++;
        }
        // 恰好两种不同质因子则输出 1,否则输出 0
        std::cout << (count == 2 ? 1 : 0) << std::endl;
    }

    return 0;
}

方法二、线性筛法(相对复杂)

C++ 53 行
#include <iostream>
#include <vector>

// 标记数组:is_primes[i]==0 表示 i 是质数;==1 表示合数
int is_primes[1000005];
// 线性筛得到的质数表,后续用于试除
std::vector<int> prime_nums;

int main() {
    int n;
    std::cin >> n;
    // 0 和 1 不是质数,先标记
    is_primes[0] = is_primes[1] = 1;

    // 线性筛(欧拉筛)预处理 1~1e6 的质数
    for (int i = 2; i <= 1000000; i++) {
        if (is_primes[i] == 0) {          // i 是质数,加入质数表
            prime_nums.push_back(i);
        }
        // 用当前质数表筛掉合数
        for (int num : prime_nums) {
            long long x = 1LL * i * num;    // 计算合数
            if (x > 1000000) {
                break;                      // 超出范围,退出
            }
            is_primes[x] = 1;               // 标记为合数
            if (i % num == 0) {
                break;                      // 保证每个合数只被最小质因子筛一次
            }
        }
    }

    // 处理每个询问
    for (int i = 0; i < n; i++) {
        int a;
        std::cin >> a;
        int count = 0;                      // 记录不同质因子个数
        // 用质数表试除,统计不同质因子
        for (int j = 0; j < prime_nums.size(); j++) {
            if (a % prime_nums[j] == 0) {   // 发现质因子
                count++;
            }
            if (count > 2) {                // 提前剪枝
                break;
            }
        }
        // 恰好两种质因子输出 1,否则 0
        std::cout << (count == 2 ? 1 : 0) << std::endl;
    }

    return 0;
}
}

💡 OneCoder 资源指引

所有代码开源上传至 GitHub:yummy-code 仓库 · GESP 专题站:GESP WIKI

🤝 技术交流与答疑

欢迎加入:C++ GESP/CSP 考级答疑群(688906745)Java/Python交流群(982860385),点击可直接加群。

📚

猜你想读 · 相关文章推荐

OneCoder

OneCoder (lihongzheshuai)

一个中年人的自留地,记录学习 C++、GESP/NOI、Java、Python 与算法架构的心得体会。本站唯一网址:coderli.com

读者讨论与留言

0 条讨论
✨ 支持点击留言直接回复 · Markdown 引用格式
💬 还没有读者留言,快来成为第一个讨论者吧!