OwenOwl's test D1T2无聊

## $Description$:

薇奈把珈百璃的电脑收走了,无聊的珈百璃只好躺在床上数数。

珈百璃想:一个数有很多很多约数,这些约数又有很多很多约数,那一个数就有很多约数的约数。

珈百璃想:12 有多少个 **约数的约数** 呢?12 的约数有1 , 2 , 3 , 4 , 6 , 12 ,这些数分别有1 , 2 , 2 , 3 , 4 , 6 个约数,那12 有18 个约数的约数。

珈百璃想:1 ~ $n$ 的数里哪个数的约数的约数个数最多呢?
没了电脑的珈百璃数不出来(虽然她一有电脑就会打网游),于是她请求你帮忙。

## $Input$:


一个整数$n$。
## $Output$:


输出共两行。

第一行一个正整数表示约数的约数个数最多的数,如果有多个输出最小的。

第二行一个正整数表示这个数的约数的约数的个数。
## $Sample$ $I$:


### $Input$:


15
### $Output$:


12

18
## $Sample$ $II$:


### $Input$:


114514
### $Output$:


110880

3402
## $Constraint$:


| 测试点 |范围 |
| :----------:|:----------: |
| $1$ |$n<=10$ |
| $2$ |$n<=10^3$ |
| $3$ |$n<=10^5$ |
| $4$ |$n<=10^7$ |
| $5$ |$n<=10^9$ |
| $6,7$ |$n<=10^{13}$ |
| $8,9,10$| $n<=10^{18} $ |

## $Solution$ :


蒟蒻的我在考试的时候看到这道题目,二话不说直接观察数据点,发现可以使用打表过至少前4个点,大致做法就是先预处理出$1$~$10^7$中每个数的约数的个数(枚举当前这个数的倍数去更新其以后的数的约数个数,去注意要加上1和它本身)。

然后再用类似的原理更新其约数的约数和。复杂度是

$O(n*(1+1/2+1/3+...+1/n))approx O(nlog_n)$

打$10^7$的表还是很快的~~其实还可以骗70分,具体我不会可以找lwq大佬~~。

下面是部分分解:
```
//打表
#include<bits/stdc++.h>
using namespace std;
long long f[10000001],dp[10000001];
int main()
{
freopen("2.txt","w",stdout);
int n=10000000;f[1]=1;
for(register int i=2;i<=n;++i){
f[i]+=2;
for(register int j=2;j*i<=n;++j)
f[j*i]++;
}
dp[1]=1;
for(register int i=2;i<=n;++i){
dp[i]+=1;
for(register int j=1;i*j<=n;++j)
dp[i*j]+=f[i];
// printf("%d %lld ",i,dp[i]);
}
int s=1;
dp[n+1]=2e15;
for(register int i=2;i<=n+1;++i){
if(dp[i]>dp[i-1]){
printf("{%d,%d,%lld}, ",s,i-1,dp[i-1]);
s=i;
}
dp[i]=max(dp[i],dp[i-1]);
}
}
```
```
#include<bits/stdc++.h>
using namespace std;
int ans[70][3]={/*打的表*/};
int main()
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
int n;
scanf("%d",&n);
for(register int i=0;i<=66;++i){
if(n<=ans[i][1]){
printf("%d %d",ans[i][0],ans[i][2]);
return 0;
}
}
}
```
## 正解 :


根据“唯一分解定理”可以得到对于一个数$n$,可以将其分解为

$p_1^{r1}p_2^{r2}....p_k^{rk}$

那么其约数的个数就可以表示为

$(r_1+1)(r_2+1)....(r_k+1)$

(很容易用乘法定理推出)

而我们也可以用乘法定理推出对于其任意约数

$p_1^{r1'}p_2^{r2'}...
p_k^{rk'}$,$r_i'in[0,r_i]$

显然我们不能枚举每一个$n$的约数来计算它们的约数个数,但是根据乘法分配律和结合律我们可以发现对于一个数

$ans_x= (1+2+...+(r_1 +1)) (1+2+...+(r_2 +1))...(1+2+...+(r_k +1))$

这里是难点,仔细想一想为什么这样可以表示出所有情况。。

那么其实问题基本上已经迎刃而解了,唯一要解决的就是如何才能找到1~n中符合条件的最小的数,我们发现 $prod_{i=1}^{cntpri}pri[i]$
在当$pri[i]=53$时已经大于$10^{18}$,而如果我们考虑把$53$前的一个质数换为大于$53$的质数时,对$ans_x$的贡献一定是前者更大,因为有$r_i geq r_j$。

所以一定有

$(1+2+...+(r_i +1))geq(1+2+...(r_j+1))$。

考虑 $dfs$ 时,我们最多只用dfs到$53$就一定可以求出答案了。此处要注意一个强力剪枝,就是记录前一个选择的质数的次数,后面选的质数的指数一定不能大于前一个,因为如果后一个系数比前一个大,那么在最好的情况下也是和之前的 $ans$ 相同,而题目让我们使求得的数字最小,所以后者一定与$ans$无关。也许你要问为什么不全选2呢。举个简单但是不严谨的例子,假如我们枚举了$2^7$和$2^6 imes3$则最终的答案分别是 $28$ 与 $21 imes3$ ,所以是有可能对$ans_x$有贡献的。

## $Codeing$ :


```
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
int pri[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
ll ans,ans2,n;

inline void dfs(int pos,ll now,ll sum,int pre){
if(sum>ans){
ans=sum;ans2=now;
}else if(sum==ans)
ans2=(ans2<now)?ans2:now;
for(register int i=1;i<=pre&&(long double)now*pri[pos]<=n;++i){
now*=pri[pos];
dfs(pos+1,now,sum*(i+1)*(i+2)/2,i);
}
}

int main()
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
scanf("%lld",&n);
dfs(0,1,1,100);
printf("%lld %lld",ans2,ans);
}
```

原文地址:https://www.cnblogs.com/Pluto-Xz/p/11267618.html