假设一个数n可以拆成
n
=
p
1
a
1
∗
p
2
a
2
∗
.
.
.
∗
p
k
a
k
n=p_1^{a_1} * p_2^{a_2} *...*p_k^{a_k}
n=p1a1∗p2a2∗...∗pkak,
其中p是质数,如
36
=
2
2
∗
3
2
36=2^2*3^2
36=22∗32,
那么约数的个数就有
(
a
1
+
1
)
∗
(
a
2
+
1
)
∗
.
.
.
∗
(
a
k
+
1
)
(a_1+1)*(a_2+1)*...*(a_k+1)
(a1+1)∗(a2+1)∗...∗(ak+1)个,即将是质数的约数个数+1后相乘
如36的约数个数就有
(
2
+
1
)
∗
(
2
+
1
)
=
9
(2+1)*(2+1)=9
(2+1)∗(2+1)=9个:1 2 3 4 6 9 12 18 36。
这是因为每个约数必然是由几个质数组合而成,不同的约数所选的质数组合不一样。
而可选的质数只有
a
1
a_1
a1个
p
1
p_1
p1,
a
2
a_2
a2个
p
2
p_2
p2,…,
a
k
a_k
ak个
p
k
p_k
pk。
问题转化成:从这些数中,共有多少种不同的选法。
因为第i个质数能选0~ a i a_i ai次,有 a i + 1 a_i+1 ai+1种选择,那么所有k个质数的选法就是 ( a 1 + 1 ) ∗ ( a 2 + 1 ) ∗ . . . ∗ ( a k + 1 ) (a_1+1)*(a_2+1)*...*(a_k+1) (a1+1)∗(a2+1)∗...∗(ak+1)种
所以只要用试除法统计所有数的所有质数个数,将各个数的质数个数相加得到 a 1 , . . . , a k a_1,...,a_k a1,...,ak,再用 ( a 1 + 1 ) ∗ ( a 2 + 1 ) ∗ . . . ∗ ( a k + 1 ) (a_1+1)*(a_2+1)*...*(a_k+1) (a1+1)∗(a2+1)∗...∗(ak+1)相乘,就能得到答案。
PS. int范围内约数个数最多的数,约数有1500个左右。
#include <iostream>
#include <unordered_map>
using namespace std;
long long ans = 1;
const int mod = 1e9 + 7;
int n, a;
unordered_map<int, int> primes;
int main() {
scanf("%d", &n);
while (n -- ) {
scanf("%d", &a);
for (int i = 2; i * i <= a; i ++ ) {
while (a % i == 0) {
a /= i;
primes[i] ++; // 各个数的约数个数相加
}
}
if (a > 1) primes[a] ++; // a是质数时
}
for (auto prime: primes) {
ans = ans * (prime.second + 1) % mod;
}
printf("%lld\n", ans);
return 0;
}
这边和试除法有些不同:
1、没有同时把
i
i
i和
a
/
i
a/i
a/i统计进去,这是因为这题只要统计是质数的约数。
2、为了确保把最大的质数统计进去,在结尾加了if (a > 1) primes[a] ++
。
如
105
=
3
∗
5
∗
7
105=3*5*7
105=3∗5∗7,只会在前面的循环中统计3和5,此时a=7,
7
<
5
\sqrt7<5
7<5,退出循环,不会记录下7,因此要加上这句。
至于为什么至多只有一个质数没在循环中统计进去,这是因为枚举到
a
=
i
∗
i
a=i*i
a=i∗i时,
i
+
1
i+1
i+1到
a
a
a至多只有1个质数是a的约数。如果有两个质数是a的约数,那么这两个质数相乘>a,矛盾。