CodeForce 27E: Number With The Given Amount Of Divisors(求反素数)

点击打开题目链接

题目翻译:

给出一个数n,求一个数其因子的个数为n,如果这个数有多个,找出最小的一个,题目说明,对于一个给定的n,其答案不会超过1e18。


这个题目其实就是让求反素数,下面是反素数的概念:

反素数的定义:对于任何正整数,其约数个数记为,例如,如果某个正整数满足:对任意的正整

            数,都有,那么称为反素数。

则这个题目就是让求一个因子数等于n的最小数字。根据反素数的定义可知这个数字一定是反素数。


首先来说怎么求一个数的因子个数:

任何一个数字都可以分解质因数,对于一个数m,用中学所学的方法,如果它可以除尽2,那么就除2,继续再看它能否除尽2,直到它

除不尽了,我们在看3,然后再看5...7...11等,这样来将一个数分解成为若干质数的乘积。如下图所示:


一个数m可以分解为    m = 2^t1*3^t2*5^t3*7^t4......      (t1>=t2>=t3>=t4>=......>=tn)

则数m的因子个数为  (t1+1)*(t2+1)*(t3+1)*(t4+1)*.......*(tn+1)

例如    12  = 2×2×3

则     12的因子个数为  1 ×(2+1)×(1+1) = 3*2 = 6    

12的因子有: 1   2  3   4  6  12  可以看出答案是正确的。那么为什么会是这样的。

这主要是和这些质因子的组合有关。12 由两个2,和一个3连乘得到

则组合情况有:0个2和0个3乘    1

                            1个2和0个3乘    2

                            2个2和0个2乘    4

                            0个2和1个3乘    3

                            1个2和1个3乘    6

                            2个2和1个3乘   12

所以对于  m = 2^t1*3^t2*5^t3*7^t4......      (t1>=t2>=t3>=t4>=......>=tn)

有t1个2,则我们可以取 0个2,1个2,2个2  .....至多t1个2与后面的相乘,则2的取法就是t1+1种,同理3的取法为t2+1种。

因此挑出分解质因子中的若干项就能得到不同的因子。由此可以证明:

m的因子个数等于   (t1+1)*(t2+1)*(t3+1)*(t4+1)*.......*(tn+1)   这个式子是正确的。

图中例子:

则138600 = 2×2×2×3×3×5×5×7×11 =  pow(2,3)*pow(3,2)*pow(5,2)*pow(7,1)*pow(11,1)

pow(x,y)  是求x的y次方的值。

这样的话138600的因子个数 = (3+1)×(2+1)×(2+1)×(1+1)×(1+1) = 4*3*3*2*2 = 144


知道了上面这些,可以来看这个题目,求一个因子个数为n的最小数字x。x不超过1e18.则我们用搜索进行暴力求解。制出素数表,取个数不同的若干素数

相乘得到某个数,并可利用上面的公式来计算这个数的因子个数。对此我们需要确定素数表应该打到多少,如果一个数都是由素数的一次幂相乘得来。

前17个素数的乘积超过答案所在范围,则我们取前16个素数制表即可,再考虑极限情况,如果一个数只由若干个2相乘得到,则2^63次方就会超出范围,

因此则其他素数的取的个数必然不会超过63.  

ans   用来存放答案,  pos  是取第pos位置上的素数,  value 当前值    num当前值value的因子个数。

递归结束情况:要求的是因子个数为n的数。

则如果当前值value的因子个数比n大,或者当前要取的素数是第十七个素数(当然这个素数不存在)这样的话返回

如果当前值value的因子个数是n,可以让其与ans比较,来更新ans.


AC代码:

#include <iostream>
#include <stdio.h>

using namespace std;

typedef unsigned long long ull;
ull ans;
int n;
///制素数表
int prime[] = {2,3,5,7,11,13,17,19,23,29,31,37,41,47,53};
///pos代表取第pos位置上的素数,value代表当前值,num为值value的因子个数
void dfs(int pos,ull value,int num)
{
    if(num > n || pos > 15)   ///因子个数大于n或将要取第17个素数都返回
        return;
    if(num == n)       ///因子个数为n的时候,看是否更新ans,并返回
    {
        if(value < ans)
            ans = value;
        return;
    }
    /**然后就考虑pos位置上的素数取i个,这里也可以做些优化,如果这里value乘上
    pos位置上的素数超过ans,则不用考虑,因为我们要的是最小的ans,value > ans/prime[pos]
    是因为,直接用value*prime[pos]可能面临溢出。pos位置上的因子取i个时,因子个数
    变成num*(i+1),这个值不仅不能大于n,而且它是n的因子。**/
    for(int i = 1; i <= 63; i++)
    {
        if(value > ans/prime[pos] || num*(i+1)>n)
            break;
        value = value*prime[pos];
        if(n%(num*(i+1)) == 0)
            dfs(pos+1,value,num*(i+1));
    }
}
int main()
{
    while(~scanf("%d",&n))
    {
        ans = 1e19;  ///这个数设得大一些。
        dfs(0,1,1);
        cout<<ans<<endl;
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值