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

C++ 算法考级专栏

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

🎨 视觉封面

【GESP/CSP练习】GESP五级 / CSP-J 题解:luogu-P1075 [NOIP2012 普及组] 质因数分解

📅 2026-09-19·✍️ OneCoder·计算中...·⏱️ 10 分钟
#NOIP#洛谷#C++#数论#质数#因数分解#GESP五级#CSP-J

NOIP 2012 普及组第一题,洛谷 P1075。本题是算法竞赛与等级考试中极为经典的初等数论与质因数分解启蒙代表作,标准收录于 CCF GESP 五级考纲(初等数论:质数判定、因数分解与欧几里得算法考点)以及 CSP-J 普及组数论基础必做题单。题目核心考察如何利用正整数唯一分解定理与因数成对对称分布规律,避开低效盲目枚举,仅通过单层循环试除较小因数便能以 O(n)\mathcal{O}(\sqrt{n}) 的高效率瞬时求出较大质因子。题目难度⭐☆☆☆☆,洛谷难度等级评定为普及-

luogu-P1075 [NOIP2012 普及组] 质因数分解

🔗 洛谷原题传送门luogu-P1075 [NOIP2012 普及组] 质因数分解

题目描述

已知正整数 nn 是两个不同的质数的乘积,试求出两者中较大的那个质数。

输入格式

输入一个正整数 nn

输出格式

输出一个正整数 pp,即较大的那个质数。

输入输出样例

输入 #1

Code 1 行
21

输出 #1

Code 1 行
7

说明/提示

1n2×1091 \le n\le 2\times 10^9

NOIP 2012 普及组 第一题


题目深度剖析

1. 数学本质与题设条件转化

题目明确指出了输入数据 nn 的核心代数特征:

  • 正整数 nn两个不同质数的乘积,即: n=p×q(pq,且 p,qP)n = p \times q \quad (p \ne q,且\ p, q \in \mathbb{P})
  • 我们的目标是求出两者中较大的那个质数,不妨设 2p<q2 \le p < q,则待求答案即为 qq

根据初等数论中的算术基本定理(唯一分解定理),任何大于 11 的自然数都可以唯一分解为有限个质数的乘积。在本题中,nn 的质因数分解形式极其特殊,恰好仅有两个质因子 ppqq

这也意味着,正整数 nn 的全部正因数集合是完全确定的,只有且仅有以下 44 个数: Div(n)={1,p,q,n}\operatorname{Div}(n) = \{ 1, p, q, n \}

2. 因数对称性与试除上界推导

在自然数的因数分解中,因数总是成对出现的。 因为 n=p×qn = p \times qp<qp < q,我们可以通过反证法推导出较小因数 pp 的取值上界:

  • 若假定 pnp \ge \sqrt{n},由于 q>pq > p,则必有 q>nq > \sqrt{n}
  • 两式相乘得到 p×q>n×n=np \times q > \sqrt{n} \times \sqrt{n} = n,这与题设 p×q=np \times q = n 产生不可调和的矛盾!
  • 因此,较小的质因数 pp 必然严格满足: 2p<n2 \le p < \sqrt{n} 对应地,较大的质因数 qq 必然严格满足: q=np>nq = \frac{n}{p} > \sqrt{n}

题目给定的数据范围为 1n2×1091 \le n \le 2 \times 10^9n2×10944721.36\sqrt{n} \le \sqrt{2 \times 10^9} \approx 44721.36 也就是说,较小的那个质数 pp 的取值范围最大绝对不会超过 4472144721

3. 为什么“找到的第一个因数一定是质数”?

许多初学者会产生一个疑问:我们在代码中直接从小到大枚举整数 ii,如果 nn 能够整除 ii,我们怎么能够保证这个 ii 一定是质数,而不是某个合数呢?

我们可以用初等数论中的经典反证法进行严格论证:

  1. 我们从 i=2i = 2 开始递增枚举,假设第一个能够整除 nn 的整数是 i0i_0
  2. 假设 i0i_0 是一个合数,那么根据合数的定义,i0i_0 必然存在一个严格介于 11i0i_0 之间的质因子 dd(即 1<d<i01 < d < i_0di0d \mid i_0);
  3. 因为 di0d \mid i_0i0ni_0 \mid n,根据整除的传递性,必然有 dnd \mid n
  4. 然而,d<i0d < i_0,且我们在循环中是从小到大依次枚举所有因数的;如果存在 dnd \mid n,循环在枚举到 dd 时就已经命中并返回了,绝不可能越过 dd 而先在 i0i_0 处第一次整除 nn
  5. 这一矛盾证明:任何大于 11 的合数,其能够被整除的最小因数必然是质数

再结合前面推导出的结论:nn 大于 11 的因数只有 p,q,np, q, n,且 p<q<np < q < n。因此,从 22 开始从小到大遇到的第一个因数,必然且只能是质数 pp

由此得出极其优雅的算法设计:

  • 我们根本不需要单独编写质数判定函数(如 isPrime(),也无需预先打表质数筛;
  • 只要在单层循环中试除找到第一个因数 ii,直接计算 ni\frac{n}{i} 即为较大质数 qq,立即输出并终止程序!

4. 算法演进与考场博弈对比

在 CCF GESP 五级和 CSP-J 考场中,面对本题有几种常见的实现思路对比:

算法方案 核心实现思路 时间复杂度 运算次数 (n=2×109n=2\times 10^9) 洛谷评测结果
反向暴力枚举 n1n - 1 向下递减枚举 qq,判断是否整除且为质数 O(n)\mathcal{O}(n) 最坏达 10910^9 次运算 超时 TLE (仅得 60 分)
盲目预处理筛法 先开数组用埃氏筛/欧拉筛预处理质数表,再查找因数 O(N)\mathcal{O}(N) 空间与时间 数组需开到 2×1092\times 10^9,爆内存 内存超限 MLE (空间爆 128MB)
正向试除因数法 (最优解) 22n\sqrt{n} 递增枚举试除,命中立即输出 ni\frac{n}{i} 退出 O(n)\mathcal{O}(\sqrt{n}) 最多仅需 4.4×1044.4 \times 10^4 次运算 最优解 100 分 AC (耗时 < 1ms)

可以看出,从较小端切入求解,利用代数关系 ni\frac{n}{i} 转化求较大端,是初等数论中最核心的“对称化简思维”。

5. 数据规模与整型溢出避坑点

  1. 循环判断条件的溢出隐患

    • 题目给定 n2×109n \le 2 \times 10^9
    • 32 位有符号整型 int 的上限为 2311=21474836472.14×1092^{31} - 1 = 2147483647 \approx 2.14 \times 10^9
    • 若将循环变量 ii 声明为 int,并写作 for (int i = 2; i * i <= n; ++i): 当 ii 递增到 4634146341 附近时,i×i2.147×109i \times i \approx 2.147 \times 10^9 会直接超出 32 位有符号整型的表示范围,发生有符号整型溢出(Integer Overflow)!在 C++ 标准中这属于未定义行为(Undefined Behavior),通常会导致计算结果变为负数,使循环陷入不可控的死循环。
    • 安全规范解法:统一使用 64 位长整型 long long 存储变量 nn 与循环变量 ii,彻底杜绝乘法溢出。
  2. 遵循 CCF GESP 官方大纲规范

    • 严禁在机考中引入不必要的复杂快读样板;
    • 严禁使用局部变长数组(VLA);
    • 代码风格保持干净、通透与结构化。

完整参考代码 (C++11)

C++ 35 行
/**
 * Problem: luogu-P1075 [NOIP2012 普及组] 质因数分解
 * Algorithm: 初等数论 / 因数分解 (Prime Factorization)
 * Standard: C++11 (CCF GESP 官方大纲规范)
 * Author: OneCoder
 */

#include <iostream>

using namespace std;

int main() {
    // 读入正整数 n (数据规模保证 1 <= n <= 2 * 10^9)
    // 采用 64 位长整型 long long 存储,避免因 i * i 运算超出 32 位整型上限而导致溢出死循环
    long long n;
    cin >> n;

    // 根据初等数论与正整数唯一分解定理:
    // n 是两个不同质数 p 和 q 的乘积 (设 2 <= p < q)
    // 根据因数成对分布的对称性,较小的质数 p 必然满足:p <= sqrt(n)
    // 且 n 仅有 1, p, q, n 这 4 个因数
    // 任何大于 1 且能整除 n 的最小正整数,必然就是较小的质数 p
    // 因此从 i = 2 开始自小到大枚举因数
    for (long long i = 2; i * i <= n; ++i) {
        if (n % i == 0) {
            // 找到较小的质因数 i (即 p)
            // 则较大的质因数必然等于 n / i (即 q)
            // 直接输出较大的质数并结束程序
            cout << n / i << endl;
            return 0;
        }
    }

    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 引用格式
💬 还没有读者留言,快来成为第一个讨论者吧!