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

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

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

反素数

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

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

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

所以,有以下两个结论 :

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

即如果 n=2q12q22q3... n = 2 q 1 ∗ 2 q 2 ∗ 2 q 3 ∗ . . . ,那么 q1>=q2>=q3... q 1 >= q 2 >= q 3 . . .


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

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

算法思路

n=pq11pq22...pqkk n = p 1 q 1 ∗ p 2 q 2 . . . p k q k
n的因子数为 (q1+1)(q2+1)...(qk+1) ( q 1 + 1 ) ∗ ( q 2 + 1 ) ∗ . . . ∗ ( q k + 1 )

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

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

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

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

因子的个数可以用上面注释部分的公式来推,假设之前已经有 j j 个了,现在乘了ik,因子数便是 j(k+1) 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);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值