最多的约数(divisor)

题目简述:

求1~N 的数中因子是最多的数

Sample Input
100
Sample Output
60
Data Constraint
Hint
有30%的数据,n不超过1000。
有50%的数据,n不超过1000000。
有100%的数据,1 ≤ n ≤ 10^16

The Solution

我们很容易想到暴力,但毋庸置疑这是超时的。。

so go on…

Algorithm1:

我们可以知道,计算约数的个数和质因数分解有着很大的联系:

若Q的质因数分解为: Q=p1k1p2k2pmkmp1pmk1km1Q(k1+1)(k2+1)(km+1)

但是质因数分解的时间复杂度很高,所以也会超时。

Algorithm2

通过以上的公式,我们可以“突发奇想”:为何不能把质因数分解的过程反过来呢?

这个算法就是枚举每一个素数。
初始时让m=1,然后从最小的素数2开始枚举,
枚举因子中包含0个2、1个2、2个2…k个2,直至 m2k 大于区间的上限N。
在这个基础上枚举3、5、7……的情况,算出现在已经得到的m的约数个数,
同时与原有的记录进行比较和替换。直至所有的情况都被判定过了。

这个算法的优化:
如果 p1p2p3pk>N (pi表示第i个素数),那么只要枚举到p k-1,既不浪费时间,也不会遗漏。

根据以上的算法,对于上限需要枚举436555171个数,也会超时,不过可以过70-80%的数据。

Algorithm3

以上的算法还不是最好的,还可以继续优化。

我们看以下的例子:

6=23
10=25

6和10的质因数分解“模式”完全相同,所以它们的约数个数是相同的。
但是由于3<5,所以6<10。

12=223
18=322

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;
}

你可能感兴趣的:(数论,mathematic,数学)