初学反素数的时候总会感到很迷茫,因为网上你找来找去只有ACdreamer一个版本的博客。我查阅了很多资料,也问过几位学长,才知道,重点不是反素数。
怎么说呢,你不能把反素数当成卡特兰数这种以数为中心的东西,其实反素数在我看来更像是一种数论。关键不是去求这个反素数,而是靠反素数的定义带来的思想做题。
反素数
#define Y(i) i的因数的个数
我们把n称为反素数如果
为了让n满足这个式子,我们需要让n的因子数多的前提尽量让n小,让n的因子分布尽量密集
所以,有以下两个结论 :
- n的因子从小开始且尽可能的连续(2,3,5,7,11…)
- 如果可以让一个因子多重复出现几次,那么一定会选择小的因子
即如果 ,那么
你会发现你遇到的题目和上面的那句话很相似
- Y(i)=n,求min(i)
- i<=n,求max(Y(i))
算法思路
n的因子数为
假设现在求的是n个因数的最小数
建一棵树,除root所在的层以外,每一层都由一个素数和其i次幂构成
我们暴力跑一遍dfs,跑到一个节点就乘上一个结点上的数
扫描二维码关注公众号,回复:
2965123 查看本文章
跑的时候记录的有 : 当前因子的个数 , 累乘后数的大小 , 跑了几层(几个素数)
因子的个数可以用上面注释部分的公式来推,假设之前已经有 个了,现在乘了 ,因子数便是
前面两题是用于熟悉算法的,真正的模板应该是是第三题,因为添加了一个剪枝
例题1 : 拥有n个因子的最小整数
#include<bits\stdc++.h>
using namespace std;
typedef long long LL;
#define debug(i) printf("->%d\n",i)
LL pri[16] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
//为什么就算是1e18也只需要这些数就够了呢? 因为越往后,前面的次方就越多
LL ans,n;
void dfs(LL Y,LL dep,LL num){
//因子数 深度 当前的数
if(dep==16)return;
if(Y==n)ans=min(ans,num);
if(Y>=n)return;
for(int i=1;i<=63;i++){
if(ans/pri[dep]<num)break;
//之前的答案更优,且可以避免爆long long
dfs(Y*(i+1),dep+1,num*=pri[dep]);
//pri^i比pri^(i-1)多一个pri,所以每次乘一个pri
}
}
int main(){
while(scanf("%lld",&n)==1){
ans=2e18;
dfs(1,0,1);
printf("%lld\n",ans);
}
}
例题2 : n以内的最多因子数的数 n=1e16
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long LL;
#define debug(i) printf("->%d\n",i)
LL pri[16] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
LL ans,n,MAXY;//MAXY表示当前的最优因子数
void dfs(LL Y,LL dep,LL num){
//因子数 深度 当前的数
if(dep==16)return;
//if(Y>=n)return;
if(Y>MAXY)MAXY=Y,ans=num;
if(Y==MAXY)ans=min(ans,num);//题目要求多个ans取小的
for(int i=1;i<=63;i++){
if(n/pri[dep]<num)break;//保证不超过n
dfs(Y*(i+1),dep+1,num*=pri[dep]);
}
}
int main(){
while(scanf("%lld",&n)==1){
ans=1e18;
MAXY=0;
dfs(1,0,1);
printf("%lld\n",ans);
}
}
例题3 :n以内的最多因子数的数 n=1e18
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL pri[16] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
LL ans,n,MAXY;
//求n以内因子最多的数,多个答案输出小的
void dfs(LL Y,LL dep,LL num,LL limit){
//因子数 深度 当前的数
if(dep==16)return;
//if(Y>=n)return;
if(Y>MAXY)MAXY=Y,ans=num;
if(Y==MAXY)ans=min(ans,num);
for(int i=1;i<=limit;i++){
//因为后面的次数不会超过前面,所以这里添加上限就是一个极大的剪枝
if(n/pri[dep]<num)break;//保证不超过n
dfs(Y*(i+1),dep+1,num*=pri[dep],i);
}
}
int main(){
int t;cin>>t;
while(t--){
scanf("%lld",&n);
ans=1e18;
MAXY=0;
dfs(1,0,1,60);
printf("%lld %lld\n",ans,MAXY);
}
}