题解:学而思编程 第K个约数
【题目来源】
学而思编程:第K个约数
【题目描述】
小猴最近在学习约数,课后猴帅老师给小猴布置了一个任务:有 \(n\) 个正整数 \(a_1,a_2,…,a_n\),这些数的和记为 \(m\),请你找出 \(m\) 的约数中从小到大的第 \(k\) 个约数。
【输入】
第一行包含两个整数 \(n,k\)。
第二行包含 \(n\) 个整数 \(a_1,a_2,…,a_n\)。
【输出】
输出一个整数,表示 \(m\) 的约数中从小到大的第 \(k\) 个约数,如果 \(m\) 不存在第 \(k\) 个约数输出 \(−1\)。
【输入样例】
5 3
1 2 3 4 5
【输出样例】
5
【核心思想】
-
问题分析:给定 \(n\) 个正整数,求它们的和 \(m\),然后找出 \(m\) 的第 \(k\) 个约数(从小到大排序)。这是一个因子枚举 + 配对收集 + 排序问题,关键在于利用因子配对性质高效枚举所有约数,再排序取第 \(k\) 个。
-
算法选择:
- 求和:先计算 \(m = a_1 + a_2 + \ldots + a_n\)
- 因子配对枚举:遍历 \(i\) 从 \(1\) 到 \(\sqrt{m}\),若 \(i \mid m\) 则同时记录 \(i\) 和 \(m/i\)
- 排序查询:将所有因子排序后取第 \(k\) 个
-
关键步骤:
- 初始化:读取 \(n\) 和 \(k\)
- 计算总和:
m = 0- 遍历 \(i\) 从 \(1\) 到 \(n\):
- 读取 \(x\)
m += x
- 枚举因子:
cnt = 0- 遍历 \(i\) 从 \(1\) 到 \(\sqrt{m}\)(即
1ll * i * i <= m):- 若
m % i == 0:p[++cnt] = i:记录小因子- 若
i != m / i:p[++cnt] = m / i:记录配对的大因子(避免重复)
- 若
- 排序并查询:
sort(p + 1, p + cnt + 1):所有因子升序排序- 若
k <= cnt:返回p[k] - 否则:返回 \(-1\)
- 输出结果
-
时间/空间复杂度:
- 时间复杂度:\(O(\sqrt{m} \log \sqrt{m})\),枚举 \(O(\sqrt{m})\),排序 \(O(d \log d)\)(\(d\) 为因子个数)
- 空间复杂度:\(O(d)\),存储因子数组
-
因子配对枚举的核心思想:
- 配对性质:若 \(i\) 是 \(m\) 的因子,则 \(m/i\) 也是因子。只需枚举到 \(\sqrt{m}\) 即可找到所有因子对,将 \(O(m)\) 降为 \(O(\sqrt{m})\)
- 去重处理:当 \(m\) 为完全平方数时,\(i = m/i\),需用
if (i != m/i)避免重复记录 - 离线排序:收集所有因子后统一排序,确保从小到大取第 \(k\) 个
- 边界判断:因子总数可能少于 \(k\),需检查
k <= cnt否则返回 \(-1\) - 适用于"因子枚举 + 排序查询"类问题,配对枚举是经典优化策略
【算法标签】
约数
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 100005; // 定义最大数组长度
int n, k; // 数组元素个数n,查询第k个因子
long long m, p[N]; // 总和m,因子数组p
long long kth() // 查找第k个因子的函数
{
int cnt = 0; // 因子计数器
for (int i=1; 1ll*i*i<=m; i++) // 枚举可能的因子
{
if (m%i==0) // 如果i是m的因子
{
p[++cnt] = i; // 记录因子i
if (i!=m/i) p[++cnt] = m/i; // 记录配对的因子(如果不同)
}
}
sort(p+1, p+cnt+1); // 对所有因子进行排序
if (k<=cnt) return p[k]; // 如果存在第k个因子,返回它
return -1; // 否则返回-1表示不存在
}
int main() // 主函数
{
scanf("%d%d", &n, &k); // 输入元素个数n和查询的k
for (int i=1; i<=n; i++) // 读取所有元素
{
int x; // 临时变量存储每个元素
scanf("%d", &x); // 输入一个元素
m += x; // 累加到总和m
}
printf("%lld\n", kth()); // 输出第k个因子
return 0; // 程序正常结束
}
【运行结果】
5 3
1 2 3 4 5
5
浙公网安备 33010602011771号