反素数(其实重点不是反素数。。。)

初学反素数的时候总会感到很迷茫,因为网上你找来找去只有ACdreamer一个版本的博客。我查阅了很多资料,也问过几位学长,才知道,重点不是反素数。

怎么说呢,你不能把反素数当成卡特兰数这种以数为中心的东西,其实反素数在我看来更像是一种数论。关键不是去求这个反素数,而是靠反素数的定义带来的思想做题。

反素数

#define Y(i) i的因数的个数

我们把n称为反素数如果 Y ( n ) > Y ( i ) ( i < n )

为了让n满足这个式子,我们需要让n的因子数多的前提尽量让n小,让n的因子分布尽量密集

所以,有以下两个结论 :

  1. n的因子从小开始且尽可能的连续(2,3,5,7,11…)
  2. 如果可以让一个因子多重复出现几次,那么一定会选择小的因子

即如果 n = 2 q 1 2 q 2 2 q 3 . . . ,那么 q 1 >= q 2 >= q 3 . . .


你会发现你遇到的题目和上面的那句话很相似

  1. Y(i)=n,求min(i)
  2. i<=n,求max(Y(i))

算法思路

n = p 1 q 1 p 2 q 2 . . . p k q k
n的因子数为 ( q 1 + 1 ) ( q 2 + 1 ) . . . ( q k + 1 )

假设现在求的是n个因数的最小数

建一棵树,除root所在的层以外,每一层都由一个素数和其i次幂构成
这里写图片描述

我们暴力跑一遍dfs,跑到一个节点就乘上一个结点上的数

扫描二维码关注公众号,回复: 2965123 查看本文章

跑的时候记录的有 : 当前因子的个数 , 累乘后数的大小 , 跑了几层(几个素数)

因子的个数可以用上面注释部分的公式来推,假设之前已经有 j 个了,现在乘了 i k ,因子数便是 j ( k + 1 )


前面两题是用于熟悉算法的,真正的模板应该是是第三题,因为添加了一个剪枝

例题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);
    }
}


猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/81989026
今日推荐