题解:AcWing 869 试除法求约数

【题目来源】

AcWing:869 试除法求约数 - AcWing题库

【题目描述】

给定 \(n\) 个正整数 \(a_i\),对于每个整数 \(a_i\),请你按照从小到大的顺序输出它的所有约数。

【输入】

第一行包含整数 \(n\)

接下来 \(n\) 行,每行包含一个整数 \(a_i\)

【输出】

输出共 \(n\) 行,其中第 \(i\) 行输出第 \(i\) 个整数 \(a_i\) 的所有约数。

【输入样例】

2
6
8

【输出样例】

1 2 3 6
1 2 4 8

【核心思想】

  1. 问题分析:给定 \(n\) 个正整数 \(a_i\),对每个数需要按升序输出其所有约数。约数具有成对出现的性质:若 \(d\)\(x\) 的约数,则 \(x/d\) 也是 \(x\) 的约数,且两者中必有一个 \(\leq \sqrt{x}\)。因此只需枚举 \([1, \sqrt{x}]\),即可在 \(O(\sqrt{x})\) 时间内找出所有约数。

  2. 算法选择

    • 试除法枚举约数:对于每个 \(a_i\),枚举 \(d\)\(1\)\(\lfloor\sqrt{a_i}\rfloor\),若 \(a_i \bmod d = 0\),则 \(d\)\(a_i/d\) 均为约数。时间复杂度 \(O(n\sqrt{a_i})\),实现简单高效
    • 排序输出:由于成对收集的约数无序,最后需一次 \(O(k \log k)\) 排序(\(k\) 为约数个数)保证升序输出
  3. 关键步骤

    • 读取输入\(n\)\(n\) 个正整数 \(a_i\)
    • 枚举函数 get_divisors(x)
      • 初始化结果数组 res
      • 枚举 \(i\)\(1\)\(\lfloor\sqrt{x}\rfloor\)(代码中用 i \leq x / i
      • \(x \bmod i = 0\)
        • \(i\) 加入 res
        • \(i \neq x/i\),将 \(x/i\) 也加入 res(避免平方数重复)
      • res 升序排序后输出
    • 逐数处理:对每个 \(a_i\) 调用函数并清空结果数组
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n\sqrt{a_i} + n \cdot k \log k)\),其中 \(k\) 为单个数的约数个数(通常很小,可视为 \(O(n\sqrt{a_i})\)
    • 空间复杂度:\(O(k)\),存储单个数的所有约数
  5. 试除法求约数的核心思想

    • 约数成对定理:若 \(d\)\(x\) 的约数,则 \(x/d\) 也是,且 \(d \cdot (x/d) = x\)。因此只需枚举到 \(\sqrt{x}\) 即可找到全部约数,效率提升一倍
    • 平方数去重:当 \(x\) 为完全平方数时,\(d = x/d = \sqrt{x}\),需特判避免将同一个约数加入两次
    • 与质因数分解的区别:求约数是找所有满足 \(x \bmod d = 0\)\(d\),不需要除尽因子,只需判断整除即可
    • 适用于单个或少量整数的约数枚举;对于大规模区间约数个数/和统计,应使用筛法预处理

【算法标签】

约数

【代码详解】

#include <bits/stdc++.h>
using namespace std;

int n; // 定义整数 n,表示输入的数的个数
vector<int> res; // res 用于存储一个数的所有约数

// 获取一个数的所有约数
void get_divisors(int n)
{
    for (int i = 1; i <= n / i; i++) { // 遍历 1 到 sqrt(n) 的所有整数
        if (n % i == 0) { // 如果 i 是 n 的约数
            res.push_back(i); // 将 i 加入 res
            if (i != n / i) res.push_back(n / i); // 如果 i 和 n/i 不相等,将 n/i 加入 res
        }
    }
    sort(res.begin(), res.end()); // 对 res 进行排序
}

int main()
{
    cin >> n; // 输入数的个数 n
    while (n--) { // 遍历每个数
        int x; // 定义整数 x
        cin >> x; // 输入 x
        res.clear(); // 清空 res
        get_divisors(x); // 获取 x 的所有约数
        for (auto t : res) cout << t << " "; // 输出 x 的所有约数
        cout << endl; // 换行
    }
    return 0; // 程序结束
}
#include <bits/stdc++.h>
using namespace std;

// 求n的所有约数的函数
// 参数:n - 要求约数的正整数
// 返回值:包含n所有约数的vector,按升序排列
vector<int> get_divisor(int n)
{
    vector<int> res;  // 存储约数的结果数组

    // 只需要遍历到√n,因为约数总是成对出现的
    for (int i = 1; i <= n / i; i++)  // 使用i <= n/i避免乘法溢出
    {
        // 如果i是n的约数
        if (n % i == 0)
        {
            res.push_back(i);  // i是一个约数

            // 如果i和n/i不相等,那么n/i也是一个约数
            if (i != n / i)
            {
                res.push_back(n / i);  // n/i是另一个约数
            }
        }
    }

    // 对结果进行排序,使得约数按升序排列
    sort(res.begin(), res.end());

    return res;
}

int main()
{
    int n;  // 要处理的数字个数
    cin >> n;

    // 处理每个数字
    while (n--)
    {
        int x;  // 当前要处理的数字
        cin >> x;

        // 获取x的所有约数
        auto res = get_divisor(x);

        // 输出约数
        for (auto t : res)
        {
            cout << t << " ";
        }
        cout << endl;
    }

    return 0;
}

【运行结果】

2
6
1 2 3 6
8
1 2 4 8
posted @ 2026-06-21 11:24  团爸讲算法  阅读(7)  评论(0)    收藏  举报