题目翻译:
给出一个数n,求一个数其因子的个数为n,如果这个数有多个,找出最小的一个,题目说明,对于一个给定的n,其答案不会超过1e18。
这个题目其实就是让求反素数,下面是反素数的概念:
反素数的定义:对于任何正整数,其约数个数记为
,例如
,如果某个正整数
满足:对任意的正整
数,都有
,那么称
为反素数。
首先来说怎么求一个数的因子个数:
任何一个数字都可以分解质因数,对于一个数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;
}