最多因子数(divisors)

题目大意:给出一段范围,求出这段范围内的含有最多约数个数的那个数,输出那个数和那个数的约数个数。

 

算法:数论

题意说的很清楚,最简单最直接的方法就是模拟了,但看一看那个数据规模。。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.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值