题解:AcWing 871 约数之和
【题目来源】
AcWing:871 约数之和 - AcWing题库
【题目描述】
给定 \(n\) 个正整数 \(a_i\),请你输出这些数的乘积的约数之和,答案对 \(10^9+7\) 取模。
【输入】
第一行包含整数 \(n\)。
接下来 \(n\) 行,每行包含一个整数 \(a_i\)。
【输出】
输出一个整数,表示所给正整数的乘积的约数之和,答案需对 \(10^9+7\) 取模。
【输入样例】
3
2
6
8
【输出样例】
252
【核心思想】
-
问题分析:给定 \(n\) 个正整数 \(a_i\),求它们乘积的约数之和,答案对 \(10^9 + 7\) 取模。根据唯一分解定理,乘积可分解为质因数的幂次乘积;而约数之和可由各质因数对应等比数列之和相乘得到。这是一个质因数分解 + 约数之和公式的数学问题。
-
算法选择:
- 试除法质因数分解:对每个 \(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)\) 内计算每个质因数对应的等比数列和
-
关键步骤:
- 读取输入:整数 \(n\),以及 \(n\) 个正整数 \(a_i\)
- 质因数分解(对每个 \(a_i\)):
- 遍历 \(i\) 从 \(2\) 到 \(\lfloor \sqrt{a_i} \rfloor\):
- 当 \(a_i \bmod i == 0\) 时,不断除以 \(i\),同时
primes[i]++
- 当 \(a_i \bmod i == 0\) 时,不断除以 \(i\),同时
- 若分解后 \(a_i > 1\),说明剩余部分本身是一个质因数,
primes[a_i]++
- 遍历 \(i\) 从 \(2\) 到 \(\lfloor \sqrt{a_i} \rfloor\):
- 合并统计:
primes中key为质因数,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
-
时间/空间复杂度:
- 时间复杂度:\(O(n \cdot \sqrt{A} + C)\),\(A\) 为 \(a_i\) 的最大值,\(C\) 为所有质因数指数之和。试除法部分 \(O(n \cdot \sqrt{A})\),等比数列求和部分 \(O(C)\)
- 空间复杂度:\(O(P)\),\(P\) 为不同质因数的个数,用哈希表存储
-
约数之和的核心思想:
- 唯一分解定理:任意正整数 \(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
浙公网安备 33010602011771号