题解:学而思编程 第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

【核心思想】

  1. 问题分析:给定 \(n\) 个正整数,求它们的和 \(m\),然后找出 \(m\) 的第 \(k\) 个约数(从小到大排序)。这是一个因子枚举 + 配对收集 + 排序问题,关键在于利用因子配对性质高效枚举所有约数,再排序取第 \(k\) 个。

  2. 算法选择

    • 求和:先计算 \(m = a_1 + a_2 + \ldots + a_n\)
    • 因子配对枚举:遍历 \(i\)\(1\)\(\sqrt{m}\),若 \(i \mid m\) 则同时记录 \(i\)\(m/i\)
    • 排序查询:将所有因子排序后取第 \(k\)
  3. 关键步骤

    • 初始化:读取 \(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 / ip[++cnt] = m / i:记录配对的大因子(避免重复)
    • 排序并查询
      • sort(p + 1, p + cnt + 1):所有因子升序排序
      • k <= cnt:返回 p[k]
      • 否则:返回 \(-1\)
    • 输出结果
  4. 时间/空间复杂度

    • 时间复杂度:\(O(\sqrt{m} \log \sqrt{m})\),枚举 \(O(\sqrt{m})\),排序 \(O(d \log d)\)\(d\) 为因子个数)
    • 空间复杂度:\(O(d)\),存储因子数组
  5. 因子配对枚举的核心思想

    • 配对性质:若 \(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
posted @ 2026-07-05 14:38  团爸讲算法  阅读(5)  评论(0)    收藏  举报