12.23 jzoj3566. 【GDKOI2014】阶乘

题目

这里写图片描述
第一行有一个正整数T,表示测试数据的组数。
接下来的T行,每行输入两个十进制整数n和base。

对于每组数据,输出一个十进制整数,表示在base进制下,n!结尾的零的个数。

2

10 10

10 2

2
8

对于20%的数据,n<=20,base<=16
对于50%的数据,n<=10^9,base<=10^5
对于100%的数据,1<=T<=50,0<=n<=10^18,2<=base<=10^12

题解

由进制转化的过程(不停除以进制数)可以很容易地把问题转化为n!可以除base的次数
但是n和base如此之大,以至于如果想求出阶乘然后一次一次除会爆炸
于是题解就把base分解质因数为 a1k1a2k2...amkm
然后检查每个质因数ai在n!中的贡献 cnt,于是就可以得出这个质因数最多容纳cnt/ki个base。
把所有容纳能力取个最小值即为答案。

问题在于我们在求ai在n!中的贡献时,可能需要O(nlogn)的时间:
枚举1*2*3*…*n中的每一个数j,易得ai在j中的贡献,累计所有贡献即为ai在n!中的贡献。
如果采用上述办法,时间会超限。

对于每个质因数,给n一直除a[i],并将每一次的商加起来,即为答案。
时间复杂度 O(logn)
参考:http://blog.csdn.net/hiweibolu/article/details/52658317

注意,因为数据很大,对于n,m,ans,记录质因数的数组,用来暂时作为n除a[i]的变量,Pascal 用int64,C++long long会爆

代码

var
  t,i,j,s:longint;
  n,m,ans,k,d:int64;
  a:array[0..1000000]of longint;
  b:array[0..1000000]of int64;//记录质因数小心炸范围!
begin
  readln(t);
  for i:=1 to t do
    begin
      readln(n,m);
      fillchar(a,sizeof(a),0);
      ans:=0;s:=0;
      k:=m;
      for j:=2 to trunc(sqrt(m)) do
        if (k>0)and(k mod j=0) then
          begin
            inc(s);
            b[s]:=j;
            while (k>0)and(k mod j=0) do
              begin
                inc(a[s]);
                k:=k div j;
              end;
          end;
      if k>1 then 
        begin 
          inc(s);b[s]:=k;a[s]:=1;
        end;//可能会剩下一个比√m大的质数
      for j:=1 to s do
        begin
          k:=n;d:=0;
          while (k>0) do
            begin
              k:=k div b[j];
              d:=d+k;
            end;
          if (ans=0)or(ans>d div a[j]) then ans:=d div a[j];
        end;
      writeln(ans);
    end;
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值