题解:AcWing 871 约数之和

【题目来源】

AcWing:871 约数之和 - AcWing题库

【题目描述】

给定 \(n\) 个正整数 \(a_i\),请你输出这些数的乘积的约数之和,答案对 \(10^9+7\) 取模。

【输入】

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

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

【输出】

输出一个整数,表示所给正整数的乘积的约数之和,答案需对 \(10^9+7\) 取模。

【输入样例】

3
2
6
8

【输出样例】

252

【核心思想】

  1. 问题分析:给定 \(n\) 个正整数 \(a_i\),求它们乘积的约数之和,答案对 \(10^9 + 7\) 取模。根据唯一分解定理,乘积可分解为质因数的幂次乘积;而约数之和可由各质因数对应等比数列之和相乘得到。这是一个质因数分解 + 约数之和公式的数学问题。

  2. 算法选择

    • 试除法质因数分解:对每个 \(a_i\)\(2\) 枚举到 \(\sqrt{a_i}\),逐个提取质因数并统计指数
    • 哈希表统计指数:使用 unordered_map 合并所有数的相同质因数,累加各自的总指数
    • 约数之和公式:若乘积的质因数分解为 \(\prod p_i^{c_i}\),则约数之和为 \(\prod (1 + p_i + p_i^2 + \cdots + p_i^{c_i})\)
    • 递推求等比数列和:利用 \(t = t \times p + 1\) 的递推式,在 \(O(c_i)\) 内计算每个质因数对应的等比数列和
  3. 关键步骤

    • 读取输入:整数 \(n\),以及 \(n\) 个正整数 \(a_i\)
    • 质因数分解(对每个 \(a_i\)):
      • 遍历 \(i\)\(2\)\(\lfloor \sqrt{a_i} \rfloor\)
        • \(a_i \bmod i == 0\) 时,不断除以 \(i\),同时 primes[i]++
      • 若分解后 \(a_i > 1\),说明剩余部分本身是一个质因数,primes[a_i]++
    • 合并统计primeskey 为质因数,value 为该质因数在所有 \(a_i\) 中的总指数 \(c_i\)
    • 计算约数之和
      • 初始化 ans = 1
      • 遍历每个质因数 \((p, c)\)
        • 初始化 t = 1
        • 循环 \(c\) 次:t = (t \times p + 1) \bmod (10^9 + 7)$。递推原理:t` 初始为 \(1\)(即 \(p^0\)),每次执行后变为 \(1 + p + p^2 + \cdots + p^k\)
        • ans = ans \times t \bmod (10^9 + 7)
    • 输出答案 ans
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \cdot \sqrt{A} + C)\)\(A\)\(a_i\) 的最大值,\(C\) 为所有质因数指数之和。试除法部分 \(O(n \cdot \sqrt{A})\),等比数列求和部分 \(O(C)\)
    • 空间复杂度:\(O(P)\)\(P\) 为不同质因数的个数,用哈希表存储
  5. 约数之和的核心思想

    • 唯一分解定理:任意正整数 \(N = p_1^{a_1} p_2^{a_2} \cdots p_k^{a_k}\)
    • 约数之和公式\(\sigma(N) = \prod_{i=1}^{k} (1 + p_i + p_i^2 + \cdots + p_i^{a_i})\)。原理:每个约数对每个质因数 \(p_i\) 独立选择指数为 \(0\)\(a_i\),由乘法原理将所有选择的贡献相乘,展开后恰好是所有约数的和
    • 等比数列递推:计算 \(1 + p + p^2 + \cdots + p^a\) 时,利用递推式 \(t_{k} = t_{k-1} \times p + 1\)(其中 \(t_0 = 1\)),避免快速幂和求逆元的复杂操作,直观且高效
    • 跨数合并:多个数的乘积的质因数分解等于各自质因数分解的指数相加,因此可以用哈希表累加各质因数的总指数
    • 模运算处理:结果可能极大,每一步加法和乘法都对 \(10^9 + 7\) 取模
    • 适用于乘积约数之和、GCD/LCM 相关组合求和等问题

【算法标签】

约数

【代码详解】

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

// 定义常量 N 和 mod
const int N = 105, mod = 1e9 + 7;

// 使用 unordered_map 来存储每个质数及其出现的次数
unordered_map<int, int> primes;

// 定义变量 n 和 a
int n, a;

int main()
{
    // 读取输入的整数 n
    cin >> n;

    // 循环 n 次,每次读取一个整数 a
    while (n--)
    {
        cin >> a;

        // 对 a 进行质因数分解
        for (int i = 2; i <= a / i; i++)
        {
            // 如果 a 能被 i 整除,则将 a 除以 i,并增加 i 的计数
            while (a % i == 0)
            {
                a /= i;
                primes[i]++;
            }
        }

        // 如果 a 大于 1,则 a 本身是一个质数,增加其计数
        if (a > 1) primes[a]++;
    }

    // 初始化答案 ans 为 1
    long long ans = 1;

    // 遍历 primes 中的每个质数及其出现次数
    for (auto p : primes)
    {
        long long k = p.first, v = p.second;
        long long t = 1;

        // 计算 (k^v + k^(v-1) + ... + k + 1) % mod
        while (v--) t = (t * k + 1) % mod;

        // 更新答案 ans
        ans = ans * t % mod;
    }

    // 输出最终答案
    cout << ans << endl;
    return 0;
}

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

#define int long long  // 将int定义为long long类型,防止溢出
const int mod = 1e9 + 7;  // 模数,用于取模运算

signed main()  // 因为使用了#define int long long,所以用signed main
{
    int n;  // 数字的个数
    cin >> n;

    // 使用哈希表存储质因数及其指数
    // key: 质因数
    // value: 该质因数的总指数
    unordered_map<int, int> primes;

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

        // 对x进行质因数分解
        for (int i = 2; i <= x / i; i++)  // 只检查到√x
        {
            if (x % i == 0)  // 如果i是x的质因数
            {
                // 计算质因数i的指数
                while (x % i == 0)
                {
                    x /= i;           // 除以质因数i
                    primes[i]++;      // 质因数i的指数加1
                }
            }
        }

        // 如果最后x>1,那么x本身是一个质因数
        if (x > 1)
        {
            primes[x]++;  // 将剩余的质因数加入哈希表
        }
    }

    int res = 1;  // 最终结果,初始化为1

    // 根据约数和公式计算结果
    for (auto prime : primes)
    {
        int p = prime.first;   // 质因数p
        int a = prime.second;  // 指数a

        // 计算 S = 1 + p + p² + ... + p^a
        int t = 1;  // 存储等比数列的和
        while (a--)
        {
            // 递推公式: t = t * p + 1
            // 初始t=1, 每次循环:
            // 第1次: t = 1 * p + 1 = p + 1
            // 第2次: t = (p+1)*p + 1 = p² + p + 1
            // 第a次: t = 1 + p + p² + ... + p^a
            t = (t * p + 1) % mod;
        }

        // 根据约数和公式: σ(N) = ∏(1 + p + p² + ... + p^a)
        res = res * t % mod;
    }

    cout << res << endl;  // 输出结果

    return 0;
}

【运行结果】

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