题解: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
【核心思想】
-
问题分析:给定 \(n\) 个正整数 \(a_i\),对每个数需要按升序输出其所有约数。约数具有成对出现的性质:若 \(d\) 是 \(x\) 的约数,则 \(x/d\) 也是 \(x\) 的约数,且两者中必有一个 \(\leq \sqrt{x}\)。因此只需枚举 \([1, \sqrt{x}]\),即可在 \(O(\sqrt{x})\) 时间内找出所有约数。
-
算法选择:
- 试除法枚举约数:对于每个 \(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\) 为约数个数)保证升序输出
-
关键步骤:
- 读取输入:\(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(避免平方数重复)
- 将 \(i\) 加入
- 对
res升序排序后输出
- 初始化结果数组
- 逐数处理:对每个 \(a_i\) 调用函数并清空结果数组
-
时间/空间复杂度:
- 时间复杂度:\(O(n\sqrt{a_i} + n \cdot k \log k)\),其中 \(k\) 为单个数的约数个数(通常很小,可视为 \(O(n\sqrt{a_i})\))
- 空间复杂度:\(O(k)\),存储单个数的所有约数
-
试除法求约数的核心思想:
- 约数成对定理:若 \(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
浙公网安备 33010602011771号