CF27E Number With The Given Amount Of Divisors 题解
Update 2022/2/3:感谢 @aresword 大佬的建议,对文章中一个不严谨但是能过题的地方进行修改。
这是一道数学题。
前置知识:
对于一个数 \(x\),我们可以将其唯一分解成 \(p_1^{c_1} \times p_2^{c_2} \times ... \times p_k^{c_k}=\prod_{i=1}^{k}p_i^{c_i}\),其中 \(p_i\) 为质数,即分解质因数。都来做这道题了这个肯定是会的吧
引理:
对于唯一分解 \(x = \prod_{i=1}^{k}p_i^{c_i}\),\(x\) 的因数个数为 \(\prod_{i=1}^{k}(c_i+1)\)。证明见文末。
那么这道题我们要怎么做呢?
这里要有贪心的思想。
对一个具有 \(n\) 个因子的数来说,肯定是 \(\text{2}\) 的个数越多越好对不对?当然我们可以举出反例,但是当因子个数不够的时候肯定是增加 \(\text{2}\) 的个数是更优的。
那么这里有一个定理:
对于一个含有 \(n\) 个正因子的最小数 \(x = \prod_{i=1}^{k}p_i^{c_i}\),如果 \(\forall i \in [1,k),p_i < p_{i+1}\),那么可以证明:
- \(p_1 = 2\)
- \(p_{i+1}\) 是 \(p_i\) 后面的第一个质数(即连续质数)
- \(\forall i \in [1,k),c_{i + 1} \leq c_i\)
证明见文末。
考虑到我们现在不能确定是否一定是 \(\text{2}\) 的个数越多越好,但是相对而言 \(\text{2}\) 的个数越多越好,那么我们采用搜索解决。
首先题目保证答案不超过 \(10^{18}\) ,而 \(2^{64} > 10^{18}\),那么我们知道指数最多为 \(\text{64}\)。又因为从 \(\text{2}\) 开始连续 \(\text{16}\) 个质数相乘已经超过 \(10^{18}\),那么我们只需要搜 \(\text{16}\) 个质数,然后加上最优化剪枝以及一些特判即可。
友情提醒:当搜索过程中的答案小于 \(\text{0}\) 的时候说明此时答案爆 long long
了,此时需要停止搜索。
这里添加一个说明:其实答案爆 long long
并不一定是答案小于 0,也可以大于 0,但是按照我的程序的逻辑答案爆 long long
和答案小于 0 互为充要条件,因此这么写是没问题的。
更正确的写法参考判断C语言的算术运算越界问题 - dennis_fan - 博客园。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 30;
int n, prime[MAXN] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53};
LL ans = 0x7f7f7f7f7f7f7f7f;
int read()
{
int sum = 0, fh = 1; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') fh = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {sum = (sum << 3) + (sum << 1) + (ch ^ 48); ch = getchar();}
return sum * fh;
}
void dfs(LL now, int k, int last, int cnt)
{
if (cnt > n) return ;
if (now <= 0ll) return ;
if (now > ans) return ;
if (k > 16) return ;
if (cnt == n) {ans = now; return ;}
for (int i = 1; i <= last; ++i)
dfs(now *= prime[k], k + 1, i, cnt * (i + 1));
}
int main()
{
n = read();
dfs(1ll, 1, 64, 1);
printf("%lld\n", ans);
return 0;
}
证明:
引理的证明:
考虑在 \(x=\prod_{i=1}^{k}p_i^{c_i}\) 中,我们将 \(p_i\) 看作 \(k\) 个箱子,在每一个箱子中我们可以取出 \(c_i+1\) 个数,分别为 \(p_i^0,p_i^1,...,p_i^k\)。
考虑到所有 \(p_i\) 都是质数,因此不会有取出的数字重复。
由乘法原理,正因子个数为 \(\prod_{i=1}^{k}(c_i+1)\)。
定理的证明:
- \(p_i=2\)。
这个其实很好想,毕竟如果我们把 \(p_1\) 换成比 \(\text{2}\) 更大的数字显然答案只会更劣。
- \(p_{i+1}\) 是 \(p_i\) 后的第一个质数。
这个也很好证明。
对于一个确定的 \((c_i,c_{i+1})\),由于规定 \(\forall i \in [1,k),p_i < p_{i+1}\),那么如果 “\(p_{i+1}\) 是 \(p_i\) 后的第一个质数” 不成立,那么 \(p_{i+1}\) 只能变得更大,答案显然更劣。
- \(\forall i \in [1,k),c_{i + 1} \leq c_i\)
由于 \(p_i < p_{i+1}\),那么我们假设 \(c_i < c_{i+1}\),这一段的答案为 \(p_i^{c_i} \times p_{i+1}^{c_{i+1}}\)。
如果我们交换指数,因子个数不变,答案为 \(p_i^{c_{i+1}} \times p_{i+1}^{c_i}\)。
此时根据前文所述,显然越大的指数要配越小的质数,那么 \(c_i < c_{i+1}\) 的答案比 \(c_{i+1} \leq c_i\) 更劣。
证毕。