题目简述:
求1~N 的数中因子是最多的数
Sample Input
100
Sample Output
60
Data Constraint
Hint
有30%的数据,n不超过1000。
有50%的数据,n不超过1000000。
有100%的数据,1 ≤ n ≤ 10^16
我们很容易想到暴力,但毋庸置疑这是超时的。。
so go on…
我们可以知道,计算约数的个数和质因数分解有着很大的联系:
若Q的质因数分解为: Q=p1k1∗p2k2∗…∗pmkm(p1…pm为素数,k1…km≥1),则Q有(k1+1)(k2+1)…(km+1)个约数。
但是质因数分解的时间复杂度很高,所以也会超时。
通过以上的公式,我们可以“突发奇想”:为何不能把质因数分解的过程反过来呢?
这个算法就是枚举每一个素数。
初始时让m=1,然后从最小的素数2开始枚举,
枚举因子中包含0个2、1个2、2个2…k个2,直至 m∗2k 大于区间的上限N。
在这个基础上枚举3、5、7……的情况,算出现在已经得到的m的约数个数,
同时与原有的记录进行比较和替换。直至所有的情况都被判定过了。
这个算法的优化:
如果 p1∗p2∗p3∗……∗pk>N (pi表示第i个素数),那么只要枚举到p k-1,既不浪费时间,也不会遗漏。
根据以上的算法,对于上限需要枚举436555171个数,也会超时,不过可以过70-80%的数据。
以上的算法还不是最好的,还可以继续优化。
我们看以下的例子:
6=2∗3
10=2∗5
6和10的质因数分解“模式”完全相同,所以它们的约数个数是相同的。
但是由于3<5,所以6<10。
12=22∗3
18=32∗2
12和18的质因数分解“模式”完全相同,所以它们的约数个数是相同的。
但是由于12的质因数分解中2的指数大于3的指数,18的质因数分解中3的指数大于2的指数,所以12<18。
根据以上的举例,我们也可以对Algorithm2中的算法进行一个改进:
可以在枚举时进行一个优化,使得枚举到的数字中2的指数不小于3的指数,
3的指数不小于5的指数……这样我们就能够得到质因数分解“模式”相同的最小数(证明略)。
再对于每一个得到的数进行比较和记录。这个算法对于上限只需要枚举34136个数,几乎达到了极限。
CODE
#include
#include
#include
#define fo(i,a,b) for (int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
int Prime[20];
bool bz[20];
ll n,Max,Num;
inline void Sieve()//线筛。。其实并无卵用。。20而已暴力绰绰有余,若问我为何。。装逼嘿嘿嘿
{
fill(bz,bz+1+20,true);
fo(i,2,20)
if (bz[i])
{
ll j = i +i;
while (j <= 20) bz[j] = false,j += i;
}
ll ii ,j;
for (ii = 2,j = 0;ii <= 20 ;ii ++)
if (bz[ii]) Prime[++ j] = ii;
}
void Search(ll number,int w,int tot,int p) /*分别代表:当前的数,当前质数,约数的个数,指数限制*/
{
if (tot > Max || (tot == Max && number < Num)) Max = tot,Num = number;
int j = 0, l = 1,q;
long long i = number;
while (j < p)
{
j++;l++;
if (n / i < Prime[w]) break;
q = tot * l;
i = i * Prime[w];
if (i <= n) Search(i,w + 1,q,j);//为什么是 j,为了是 前一个素数的指数大于后一个素数的指数
}
}
int main()
{
Sieve();
scanf("%lld",&n);
Search(1,1,1,30);
printf("%lld\n",Num);
return 0;
}