原题传送门:Number With The Given Amount Of Divisors
一、分析
1. 一个正整数的正因子个数计算方法
为了解决这道题目,我们首先需要做的当然就是推导出如何计算一个给定正整数的正因子数目。为了达成这一点,我们首先需要知道这一点,即:
【算数基本定理】任何一个大于 1 1 1 的自然数 N N N ,如果 N N N 不为质数,那么 N N N 可以唯一分解成有限个质数的乘积 N = P 1 a 1 P 2 a 2 P 3 a 3 ⋯ P n a n N=P_1^{a_1}P_2^{a_2}P_3^{a_3}\cdots P_n^{a_n} N=P1a1P2a2P3a3⋯Pnan ,这里 P 1 < P 2 < P 3 < ⋯ < P n P_1<P_2<P_3<\cdots <P_n P1<P2<P3<⋯<Pn 均为质数,其中指数 a i a_i ai 是正整数。这样的分解称为 N N N 的标准分解式。
知道了算术基本定理的内容后,我们就可以把
N
N
N 的正因子个数量化地表示出来了。由整除的性质,
N
N
N 的每个正因子均可以表示为
P
1
b
1
⋯
P
n
b
n
P_1^{b_1}\cdots P_n^{b_n}
P1b1⋯Pnbn 的形式,其中
0
≤
b
1
≤
a
1
,
…
0
≤
b
n
≤
a
n
0\leq b_1 \leq a_1,\ldots 0\leq b_n \leq a_n
0≤b1≤a1,…0≤bn≤an,也即每个指数
b
i
b_i
bi 有
0
,
…
a
i
0,\ldots a_i
0,…ai 共计
a
i
+
1
a_i+1
ai+1 种选择方式。由乘法原理,这样的数共有
∏
i
=
1
n
(
a
i
+
1
)
=
(
a
1
+
1
)
(
a
2
+
1
)
⋯
(
a
n
+
1
)
\prod_{i=1}^n (a_i+1)=(a_1+1)(a_2+1)\cdots(a_n+1)
i=1∏n(ai+1)=(a1+1)(a2+1)⋯(an+1)
个。反过来说,给定一个正整数
N
N
N 的正因子个数,我们也可以利用上面的这个公式反推回去。
2. 如何保证结果最小?
我在拿到这个问题的时候,第一感觉就是:尽量让 N N N 的唯一分解式中质数数量尽量多,否则相当于把重心放在了较小的质数上,结果也成几何形上升。
(如果上面这段话说得比较迷的话,我们举个例子:假定题目给定的数是 100 = 2 2 ⋅ 5 2 100=2^2\cdot 5^2 100=22⋅52 ,那么比起 2 99 2^{99} 299 来说,我们如果把相应的指数幂转到其它素数上,例如 2 2 2 − 1 ⋅ 3 5 2 − 1 2^{2^2-1}\cdot 3^{5^2-1} 222−1⋅352−1 ,那结果便会小得多。)
2.1 尽量让 N N N 的唯一分解式中质数数量尽量多。
光多还是不够的,如果把最大指数放到一个较大的质数上,我们不妨调整一下,把它交换小一点的质数上,这样结果也会变小。于是得到结论:
2.2 更大的指数幂放在较小的质数上。
于是我得到了第一份代码:
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <cmath>
using namespace std;
int divisor[101] = { 1 };
bool isPrime(int p) {
if (p == 0 || p == 1)
return false;
else {
for (int i = 2; i < p; i++) {
if (p % i == 0)
return false;
}
return true;
}
} // 判断素数
long long power(int p, int a) {
if (a == 1)
return p;
else {
return power(p, a - 1) * p;
}
} // 幂指数运算
int cur = 1;
void div(int n) {
if (n == 1)
divisor[cur] = 1;
else {
for (int i = n; i >= 2; i--) {
if (isPrime(i) && n % i == 0) {
divisor[cur] = i;
cur++;
div(n / i);
break;
}
}
}
} // 从大到小排列n的质因子
int main() {
long long n, ans = 1;
cin >> n;
div(n);
if (n == 1)
ans = 1;
} else {
for (int i = 1; i <= 100; i++) {
divisor[i]--;
}
int p = 2;
for (int i = 1; i <= 100; i++) {
while (!isPrime(p))
p++;
if (isPrime(p)) {
if ((divisor[i]) == 0)
break;
else {
ans *= power(p, divisor[i]);
}
}
p++;
}
}
cout << ans;
return 0;
}
结果:WA
。
根据评测系统的提示,我发现当 n = 8 n=8 n=8 时,我的程序输出结果与预期不一致,原来是调整出了问题。细心的同学可能会发现,当 n = 2 k n=2^k n=2k 时,我们的调整方法是无效的,例如说 n = 8 n=8 n=8 的情况,根据原程序,它将会返回 2 ⋅ 3 ⋅ 5 = 30 2\cdot3\cdot5=30 2⋅3⋅5=30,但显然此时如果我们将 8 8 8 拆成 4 ⋅ 2 4\cdot 2 4⋅2 而非 2 ⋅ 2 ⋅ 2 2\cdot 2\cdot 2 2⋅2⋅2,那么此时输出的结果应该是 2 3 ⋅ 3 = 24 2^3\cdot 3=24 23⋅3=24(注意,这里仍不能完全把指数幂归到 2 2 2 上面,否则就调整过度了!)
终于,我们可以得到完整 AC
代码,如下所示:
二、完整代码
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <cmath>
using namespace std;
int divisor[101] = { 1 };
bool isPrime(int p) {
if (p == 0 || p == 1)
return false;
else {
for (int i = 2; i < p; i++) {
if (p % i == 0)
return false;
}
return true;
}
}
bool isTwopower(int n) {
if (n == 2)
return true;
else {
if (n % 2 != 0)
return false;
else {
return isTwopower(n / 2);
}
}
} // 判断是否为2的幂
long long power(int p, int a) {
if (a == 1)
return p;
else {
return power(p, a - 1) * p;
}
}
int cur = 1;
void div(int n) {
if (n == 1)
divisor[cur] = 1;
else {
for (int i = n; i >= 2; i--) {
if (isPrime(i) && n % i == 0) {
divisor[cur] = i;
cur++;
div(n / i);
break;
}
}
}
}
int main() {
long long n, ans = 1;
cin >> n;
div(n);
if (n == 1)
ans = 1;
else if (isTwopower(n) && n >= 8) {
for (int i = 1; i <= 100; i++) {
divisor[i]--;
}
ans *= 8;
int p = 3;
for (int i = 3; i <= 100; i++) {
while (!isPrime(p))
p++;
if (isPrime(p)) {
if ((divisor[i]) == 0)
break;
else {
ans *= power(p, divisor[i]);
}
}
p++;
}
} else {
for (int i = 1; i <= 100; i++) {
divisor[i]--;
}
int p = 2;
for (int i = 1; i <= 100; i++) {
while (!isPrime(p))
p++;
if (isPrime(p)) {
if ((divisor[i]) == 0)
break;
else {
ans *= power(p, divisor[i]);
}
}
p++;
}
}
cout << ans;
return 0;
}