阶乘问题

该程序要求计算1到N(N<=50,000,000)阶乘的最右边的非零位。通过分析因数2和5的数量,可以确定末尾非零数字。由于2的个数总是超过5,可以用2的倍数替换5的倍数,特别是用8替换5,以减少末尾零的数量。最终通过找到的规律确定非零位。" 138316844,7337247,深度学习与AutoML:自动化机器学习的探索,"['深度学习', '神经网络', '大数据', '人工智能', 'Python', '架构设计', 'Agent']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

www.luogu.org/problemnew/show/P1134

说明:来源落谷P1134-USACO Training Section 3.2

 

题目描述

也许你早就知道阶乘的含义,N阶乘是由1到N相乘而产生,如:

12! = 1 x 2 x 3 x 4 x 5 x 6 x 7 x 8 x 9 x 10 x 11 x 12 = 479,001,600

12的阶乘最右边的非零位为6。

写一个程序,计算N(1<=N<=50,000,000)阶乘的最右边的非零位的值。

注意:10,000,000!有2499999个零。

输入输出格式

输入格式:

仅一行包含一个正整数N。

输出格式:

单独一行包含一个整数表示最右边的非零位的值。

输入输出样例

输入样例#1:                                                                               

12

输出样例#1:                                                                               

6

 

 

 

我有一个很玄学的方法。(不推荐使用)

#include<bits/stdc++.h>
using namespace std;
int main()
{
	long long n,i,ans=1;
	cin>>n;
	for(i=1;i<=n;i++)
	{
		ans*=i;
		while(ans%10==0)ans/=10;
		ans%=100000000;
	}
	cout<<ans%10;
    return 0;
}

我想不用说什么了吧!

数学方法:10是由2*5得来的。统计1~N中每个数的因数中2和5的个数.用2的个数减去5的个数(2的个数一定比5的个数多)。去掉5的个数2。保留最后一位。之后mod 10 就行了。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	long long n,i,j,s2=0,ans=1;
	cin>>n;
	//8*2=16 6*2=12 2*2=4 4*2=8所以4个一循环 
	for(i=1;i<=n;i++)
	{
		j=i;
		if(i%2==0)			//2的因数 
			while(j%2==0){
				s2++;
				j>>=1;		//相当于j/=2 
			}
		if(i%5==0)			//5的因数 
			while(j%5==0){
				s2--;		//抵消 
				j/=5;
			}
		ans=ans*j%10;		//相乘保存末尾 
		if(s2>20000){		//防止s2过大 
			s2%=400;		//4个一循环可以放心的mod 400  
			if(s2<400)s2+=400;
		}
	}
	s2%=4;					//4个一循环 
	if(s2==0)s2+=4;			//ans不是2的倍数,一定要乘2改变 
	for(i=1;i<=s2;i++)
		ans=ans*2%10;
	cout<<ans%10;
    return 0;
}

然后又发现2*4*8==4,2*4*5==4(末尾数)。我们就可以用8代替5。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,i,ans=1;
	scanf("%d",&n);		//输入 
	for(i=1;i<=n;i++)
		if(i%5==0){
			int j=i;
			while(j%5==0){	//一个5用一个8代替 
				j/=5;
				ans=ans*8%10;
			}
			ans=ans*j%10;	//放心mod 10 
		}else ans=ans*i%10;
	printf("%d",ans);
	return 0;
	
}	int n,i,ans=1;
	scanf("%d",&n);		//输入 
	for(i=1;i<=n;i++)
		if(i%5==0){
			int j=i;
			while(j%5==0){	//一个5用一个8代替 
				j/=5;
				ans=ans*8%10;
			}
			ans=ans*j%10;	//放心mod 10 
		}else ans=ans*i%10;
	printf("%d",ans);
	return 0;
	
}

最后摘抄一下dalao的思路:

(洛谷题解)分析:N!的末尾只会是2,4,6,8(在此题中没有0).又[2,4,6,8]中任意一个数乘6,末尾仍是本身.2 * 6 = 12,末尾为2, 4 * 6 =24,末尾为4, 6 * 6=36,末尾为6,8 * 6 =48,末尾为8.又末尾数字只受末尾数字影响,如4. 4 * 6得到的末尾数字与4 * 16的末尾数字一样.又4 * 10=40,末尾数字为4(此题不要末尾0).则4 * 2 *8==4 * 2 *5(意思是得到的末尾数字是一样的).故所有乘5的时候都可以换为乘8.又多次乘8末尾是有规律的.找出规律即可.

#include <cstdio>
using namespace std;
int n,ans=1;
int a[4]= {6,8,4,2};
int main() {
    scanf("%d",&n);
    while (n>0) {
        for (int i=1; i<=n%10;++i)//除了5之外,其他数字原样乘.
        //n%10 的原因:答案只受末尾数字影响 
            if (i!=5) ans=ans*i%10;//跳过乘5(此时可以放心%10) 
        n=n/5;//n/5即少乘了多少次5
        //即乘8的次数 
        ans=ans*a[n%4]%10;//四次一循环(此时可以放心%10) 
    }
    printf("%d",ans);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值