当前位置: 代码迷 >> 综合 >> xdoj 1019 自然数的秘密
  详细解决方案

xdoj 1019 自然数的秘密

热度:81   发布时间:2023-12-22 14:32:32.0

嗯...这次的题解不是找规律了...

这是题目链接:

xdoj 1019 自然数的秘密

题目大意:给出一个数值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;elsecout<<ans<<endl;}return 0; 
}