题目传送门:https://www.luogu.org/problemnew/show/P3927
题目大意:给你两个正整数n,k,求n!在k进制下末尾零的数量。
我们通过简单的数学分析,便可以发现,n!可以化为x*k^y(x,y∈N),而末尾零的数量,正是y。
经过进一步化简,$n! = x*prod_{1}^{d(k)}d_{i}*y_{i}$,其中$d_{i}$为k的素因子,此处d(k)表示k的素因子个数,在已知d的情况下,求$y$并不复杂。
同时,我们也可以将k也化为这个格式,$k = *prod_{1}^{d(k)}d_{i}*y'_{i}$。
通过进一步的归纳,我们发现:末尾零的数量为$min(left lfloor y_{i}/y'_{i} ight floor)$。此题我们只需要求出$d_{i}$,随后求出$y_{i}$和$y‘_{i}$,最后简单计算即可得出答案。
考虑到$k≤10^{12}$,如果用常规的方法进行分解质因数求出所有$d_{i}$显然不行,于是我们用pollard_rho进行分解质因数即可。
如果你不知道什么是pollard_rho,传送门:http://blog.csdn.net/maxichu/article/details/45459533
时间复杂度为$O(k^{0.25}*log(n)*d(k))$
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define L long long 5 #include<algorithm> 6 #include<map> 7 using namespace std; 8 L gcd(L x,L y){return y==0?x:gcd(y,x%y);} 9 L mul(L x,L y,L MOD){ 10 L ans=0; 11 while(y){ 12 if(y&1) ans=(ans+x)%MOD; 13 y=y>>1; x=(x<<1)%MOD; 14 } 15 return ans; 16 } 17 bool pow(L x,L y,L MOD){ 18 L ans=1; 19 while(y){ 20 if(y&1) ans=mul(ans,x,MOD); 21 y=y>>1; x=mul(x,x,MOD); 22 } 23 return ans!=1; 24 } 25 bool check(L n){ 26 if(n==2) return 1; if(n<2||n%2==0) return 0; 27 int t=20; while(t--) if(pow(1+rand()%(n-1),n-1,n)) return 0; 28 return 1; 29 } 30 L pollard_rho(L n,L c){ 31 L x=1+rand()%(n-1),y=x,k=2; 32 for(int i=1;;i++){ 33 x=(mul(x,x,n)+c)%n; 34 L d=gcd((y-x+n)%n,n); 35 if(d>1) return d; 36 if(x==y) return n; 37 if(i==k) y=x,k<<=1; 38 } 39 } 40 map<L,int> m; 41 void find(L n,L c){ 42 if(n==1) return; 43 if(check(n)){m[n]++; return;} 44 L p=n; while(p>=n) p=pollard_rho(n,c--); 45 find(p,c); find(n/p,c); 46 } 47 L get(L n,L d){ 48 L ans=0; 49 while(n) ans+=n/d,n=n/d; 50 return ans; 51 } 52 53 int main(){ 54 L n,k,minn=1e18; cin>>n>>k; find(k,2333333); 55 for(map<L,int>:: iterator it=m.begin();it!=m.end();it++){ 56 L num=it->first,sum=it->second; 57 minn=min(minn,get(n,num)/sum); 58 } 59 cout<<minn<<endl; 60 }