嗯...这次的题解不是找规律了...
这是题目链接:
题目大意:给出一个数值m,求出n!最后0的位数恰为m的最小的n的值,无解则输出“No solution”。
没错...看到题的第一想法当然是找规律啦,毕竟m的范围达到1e8且为多组数据...首先是想到假设把n!每个数都分解质因数,0的位数自然和5*2的个数有关啦,而2的个数显然远多于5,因此只需考虑分解质因数后5的个数就行了。而因数5的个数自然就和5的倍数有关了,且对于某个满足条件的最小的n,它一定是5的倍数!然后只手动打了一小部分表后没找出简单明了易实现的规律,于是开始思考正经解法。
首先要想到的是,对于某个数n,n!分解后会有多少个质因数5呢?得到的方法很简单,预先打出5的1-12次方的表,将n依次除以表中数据再相加,所得即为质因数5的个数。那么,既然已经知道了n!的质因数5的个数了,是不是也代表知道了n!后的0的个数了?答案显然是的。因此就可以想到二分答案,左端点为0,右端点为5*m(显然1-5*m中至少含有m个5的倍数)。但是二分的话,问题又来了——当m的值“No solution”呢?比如说m=5时,当n=24时,0的个数为4,当n=25时,0的个数为6,直接二分是会死循环的。那么对于这种情况,就需要考虑当二分过程中左端点等于右端点时,端点值是否是5的k次方的倍数(k=2,...,12),若是则无解,不是则有解。具体实现还是看代码吧。
AC代码如下:
#include<iostream>
using namespace std;
int m;
int f[12]={5,25,125,625,3125,15625,78125,390625,1953125,9765625,48828125,244140625};
int erfen(int l,int r){
int mid,ans,flag=0;
while(l<=r){
mid=(l+r)>>1,ans=0,flag=0;
for(int i=0;i<12;i++){
if(mid<f[i])
break;
ans+=mid/f[i];
if((mid-mid%5)%f[i]==0){
flag++;
}
}
if(l==r){
if(flag>=2)
return -1;
break;
}
if(ans<m){
l=mid+1;
}
else if(ans>m){
r=mid;
}
else if(ans==m){
break;
}
}
return mid-(mid%5);
}
int main(){
int t,tmp;
cin>>t;
while(t--){
cin>>m;
if(m==0){
cout<<0<<endl;
continue;
}
tmp=m*5;
int ans=erfen(0,tmp);
if(ans==-1)
cout<<"No solution"<<endl;
else
cout<<ans<<endl;
}
return 0;
}