193. 最多因子数
★★★ 输入文件:divisors.in 输出文件:divisors.out 简单对比
时间限制:3 s 内存限制:128 MB
【问题描述】
数学家们喜欢各种类型的有奇怪特性的数。例如,他们认为945是一个有趣的数,因为它是第一个所有约数之和大于本身的奇数。
为了帮助他们寻找有趣的数,你将写一个程序扫描一定范围内的数,并确定在此范围内约数个数最多的那个数。不幸的是,这个数和给定的范围的都比较大,用简单的方法寻找可能需要较多的运行时间。所以请确定你的算法能在几秒内完成最大范围内的扫描。
【输入格式】
只有一行,给出扫描的范围,由下界L和上界U确定,满足2≤L≤U≤1 000 000 000。
【输出格式】
对于给定的范围,输出该范围内约数个数D最多的数P。若有多个,则输出最小的那个。
请输出“Between L and U,P has a maximum of D divisors.”,其中L,U,P和D的含义同前
面所述。
【输入输出样例】
divisors.in
1000 2000
divisors.out
Between 1000 and 2000,1680 has a maximum of 40 divisors.
题解:
我们要对一段区间进行查询,很明显并不能一个一个去验证,时间会炸的飞起。
所以我们考虑对于一个数,它的约数个数是什么?
如果我们把一个数X分解质因数——>
X=2m1∗3m2∗5m3∗...∗nmk
通过乘法原理我们可以发现,它的约数个数是
(m1+1)∗(m2+1)∗(m3+1)∗...∗(mk+1)
于是我们可以从质因数来下手,因为它的范围是根号n的,然后对于每个质数进行扩展就好了。
网上有不错的文章,说的很详细,这里就不多说了,可以戳这里——> 对没错就是这里
Code:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 100001
using namespace std;
int num=0,ans=0,minn,anx,yes[N],no[N];
void xs(){
memset(yes,0,sizeof(yes));
memset(no,0,sizeof(no));
no[0]=no[1]=1;
for (int i=2; i<N; i++){
if (!no[i]) yes[num++]=i;
for (int j=0; j<num && i*yes[j]<N; j++){
no[i*yes[j]]=1;
if (!(i%yes[j])) break;
}
}
}
void work(int start,int s,int x,int L,int R){
if (x>=minn){
if ((s>ans) || (s==ans && x<anx))
ans=s,anx=x;
}
if ((L==R) && (L>x))
work(start,s<<1,x*L,1,1);
for (int i=start; i<num; i++){
if (yes[i]>R) return;
int j=yes[i],ll=L-1,l=L,r=R,y=x,ss=s,m=1;
while (1){
m++; ss+=s; ll/=j; l/=j; r/=j;
if (ll==r) break; y*=j;
work(i+1,ss,y,l,r);
}
m=1<<m;
if (s<(ans/m)) return;
}
}
int main(){
int l,r;
scanf("%d%d",&l,&r);
xs();
if (l==1 && r==1) ans=1,anx=1;
else {
minn=l,ans=2,anx=l;
work(0,1,1,l,r);
}
printf("Between %d and %d, %d has a maximum of %d divisors.\n",l,r,anx,ans);
return 0;
}