xdoj 1019 自然数的秘密

求解n!尾0数目的算法
本文探讨了一个数学问题的算法解决方法,通过分解质因数来确定阶乘尾部0的数量,并采用二分查找策略寻找最小的n值,使得n!的尾部恰好包含m个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;
		else
		cout<<ans<<endl;
	}
	return 0; 
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值