P1221 最多因子数

根据唯一分解定理 m=p1a1×p2a2×...×pkakm=p_1^{a_1}\times p_2^{a_2} \times ... \times p_k^{a_k}.

那么 mm 的约数个数就为 (a1+1)(a2+1)...(ak+1)(a_1+1)(a_2+1)...(a_k+1).

并且观察数据范围, n5×104n \le 5 \times 10^4.

那么可以得出 k16k \le 16 ,因为 2165×1042^{16} \ge 5 \times 10^4.

所以接下来就可以直接通过搜索枚举前16个质数的幂次方 aa 就行了.

但是由于答案可能很大,所以要用高精,但如果每一次更新答案都用一次高精,很明显会超时.

根据小学知识 lg(x×y)=lgx+lgy,lgxy=y×lgx\lg(x\times y)=\lg{x}+\lg{y},lg{x^y}=y\times \lg{x},我们可以对答案取一个对数,就不怕答案很大了

接下来就可以开始通过搜索来枚举 aia_i

直接爆搜肯定是不行的,所以要想办法优化一下,我们在dfs的时候传入四个参数

poipoi 表示当前枚举到第 poipoi 个质数, nownow 表示当前的质因子个数, lastlast 表示之前枚举的幂次方, temptemp 表示当前答案取完对数的结果

可以得到两个明显的剪枝

最优性剪枝:如果当前的 temptemp 已经大于之前的最小值,直接返回

可行性剪枝:在枚举幂次方 ii 时, 如果 (i+1)×now(i+1) \times now不是nn的倍数,舍掉这种情况

并且在枚举的时候,我们需要从 lastlast 开始,因为枚举出来的 aia_i 一定时单调不上升的,否则答案一定不是最优,因为我们始终可以交换任意两个 aia_i 使答案更小

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;

int prime[16] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53};
int rec[16], a[16], n;
double recv = 1e13, val[16];

struct num
{
ll data[10005];
num operator * (const int b) const
{
num c;
memcpy(c.data, data, sizeof(data));
for (int i = 1; i <= c.data[0]; i++)
c.data[i] *= b;
for (int i = 1; i <= c.data[0]; i++)
{
if (c.data[i] >= 10000)
{
c.data[i + 1] += c.data[i] / 10000;
c.data[i] %= 10000;
}
}
ll &h = c.data[0];
while (c.data[h + 1] > 0)
{
h++;
c.data[h + 1] += c.data[h] / 10000;
c.data[h] %= 10000;
}
return c;
}
void print()
{
printf("%lld", data[data[0]]);
for (int i = data[0] - 1; i >= 1; i--)
{
if (data[i] <= 9)
printf("000");
else if (data[i] <= 99)
printf("00");
else if (data[i] <= 999)
printf("0");
printf("%lld", data[i]);
}

}
} ans;//高精乘

void dfs(int poi, int now, int last, double temp)
{
if (poi == 16 || temp > recv || n % now)
return;
if (now == n)
{
if (temp < recv)
{
recv = temp;
memcpy(a, rec, sizeof(rec));
}
return;
}
double t = val[poi];
int k = n / now;
for (int i = min(k - 1, last); i >= 0; --i)
{
rec[poi] = i;
dfs(poi + 1, now * (i + 1), i, temp + i * t);
}
rec[poi] = 0;
}

int main()
{
scanf("%d", &n);
for (int i = 0; i < 16; i++)
val[i] = log(prime[i]);
dfs(0, 1, n - 1, 0);
ans.data[0] = ans.data[1] = 1;
for (int i = 0; i < 16; i++)
while (a[i]--)
ans = ans * prime[i];
ans.print();
}