题目大意:给出一段范围,求出这段范围内的含有最多约数个数的那个数,输出那个数和那个数的约数个数。
算法:数论
题意说的很清楚,最简单最直接的方法就是模拟了,但看一看那个数据规模。。1000000000。。且不说是一段区间,就是这个数字就已经够大了,所以必须采用别的方法。
很容易想到了分解质因数的方法,一个数x,如果x=p1^a1+p2^a2+p3^a3+p4^a4+...+pn^an(其中p1,p2,pn都是质因数),那么它的约数个数就是(a1+1)*(a2+1)*...*(an+1)。(原理不再解释了。)
既然这样,我们就可以先筛个质数表,然后分别枚举质因数,例如:算出一个2,两个2,三个2。。。直到超过下界为止,然后把这些质因数的个数一乘就行了。
注意乘的时候只需要到根号,过了根号就会重复。
但是显而易见的问题又来了,如果用这个质因数去组成的话最终落不到那个区间里怎么办?怎么样才能很快的判断出这点来呢?
我们可以比较(l-1) div number和r div number的大小,若这两个数相等,那么就说明从l到r这一段区间中没有一个数可以被我们枚举的number整除,因此可以直接剪掉。
然后又是一个非常强大的剪枝,设from为当前枚举到的质因子,total是当前的最多的约数个数,那么这次最多能剩下的因子数为p=log(from)(max/number),这些因子组成的约数个数最大为2^q。当前所能取到的最大约数个数就是total*2^q,如果这个数仍然无法超过最优解,那么直接剪掉这一枝。
至此,本题基本上就可以完美的解决了。
PS:除了筛素是我写的,别的都是别人的代码。。。
program divisors;
const
maxprime=31622;{最大的求质数的范围,为trunc(sqrt(1000000000))}
amount=3401;{可以求得的最多的质数个数。}
var
primes:array [0..amount] of longint;{prime记录质数,是质数表。}
l,r,number,max:longint;{l为下界,r为上界,number为最多约数个数的那个数,max为这个数的约数个数。}
procedure init;
begin
readln(l,r);
end;
procedure ss;{筛素,求质数表。}
var
i,j:longint;
get:array [2..maxprime] of boolean;
begin
fillchar(get,sizeof(get),true);
for i:=2 to maxprime do
begin
if get[i] then
begin
for j:=2 to maxprime div i do
get[i*j]:=false;
end;
end;
j:=0;
for i:=2 to maxprime do
begin
if get[i] then
begin
inc(j);
primes[j]:=i;
end;
end;
end;
procedure try_(from,tot,num,low,up:longint);{from表示当前的选择的质因数,tot表示当前的约数个数,num表示那个数,low表示下界,up表示上界。}
var
x,y,n,m,i,j,t:longint;
begin
if num>=l then
begin
if (tot>max) or ((tot=max) and (num<number)) then{能进行更新的条件。}
begin
max:=tot;
number:=num;
end;
end;
if (num<up) and (tot shl 1>max) then max:=tot shl 1;{处理sqrt那一半的质数。}
for i:=from to amount do
begin
if primes[i]>up then exit
else
begin
j:=primes[i];
x:=low-1;
y:=up;
n:=num;
t:=tot;
m:=1;
while true do
begin
inc(m);
inc(t,tot);{记录约数个数。}
x:=x div j;
y:=y div j;
if x=y then break;{剪枝1。}
n:=n*j;{表示下一个数。}
try_(i+1,t,n,x+1,y);
end;
if tot<max shr m then exit;{剪枝2。}
end;
end;
end;
procedure main;
begin
if (l=1) and (r=1) then
begin
max:=1;
number:=1;
end
else
begin
max:=2;
number:=l;
try_(1,1,1,l,r);
end;
end;
begin
assign(input,'divisors.in'); reset(input);
assign(output,'divisors.out'); rewrite(output);
init;
ss;
main;
writeln('Between ',l,' and ',r,', ',number,' has a maximum of ',max,' divisors.');
close(input); close(output);
end.