The Last Non-zero Digit(POJ - 1150)(计数好题)

本文介绍了一种计算组合数Amn最低非0位的高效算法,通过分析2、5倍数及3、7、9倍数在阶乘中的分布规律,提出了getnum2、getnum5和f、g函数,用于快速计算非0末位乘积。

文章目录

前言

很难想

题目

Vjudge
POJ
题目大意
给定 N , M N,M N,M ( 0 ≤ M ≤ N ≤ 20000000 ) (0 \le M\le N\le 20000000) 0MN20000000,计算 A m n A_m^n Amn的最低非0位

思路

我们知道 A n m = n ! ( n − m ) ! A_n^m=\frac{n!}{(n-m)!} Anm=(nm)!n!
同时 10 = 2 ∗ 5 10=2*5 10=25
也就是计算 n − m + 1 n-m+1 nm+1 ~ n n n 非0末位乘积的非0末位
那么我们知道从个位数判断2,5需要数字:2,4,5,6,8(没有0)
我们记 g e t n u m 2 ( n ) getnum_2(n) getnum2(n) n ! n! n!中 2倍数 的个数(去0)
我们记 g e t n u m 5 ( n ) getnum_5(n) getnum5(n) n ! n! n!中 5 的个数(去0)
那么:
g e t n u m 2 ( n ) = n / 2 + g e t n u m 2 ( n / 2 ) getnum_2(n)=n/2+getnum_2(n/2) getnum2(n)=n/2+getnum2(n/2)
g e t n u m 5 ( n ) = n / 5 + g e t n u m 5 ( n / 5 ) getnum_5(n)=n/5+getnum_5(n/5) getnum5(n)=n/5+getnum5(n/5)
我们可以这样理解:
1 ∗ 2 ∗ 3 ∗ 4 ∗ 5 ∗ 6 ∗ 7 ∗ 8 ∗ 9 ∗ 10..... 1*2*3*4*5*6*7*8*9*10..... 12345678910.....
其中2的倍数为,个数为 n / 2 n/2 n/2
2 ∗ 4 ∗ 6 ∗ 8 ∗ 10..... 2*4*6*8*10..... 246810.....
整体除以2后又是原来的样子,只不过数量减半
5同理
那我们只需要处理 3,7,9 了
我们记 f ( n , x ) f(n,x) f(n,x) n ! n! n! 中 各个数字末位为 x x x倍数 的个数(去0)
我们再来看看
1 ∗ 2 ∗ 3 ∗ 4 ∗ 5 ∗ 6 ∗ 7 ∗ 8 ∗ 9 ∗ 10..... 1*2*3*4*5*6*7*8*9*10..... 12345678910.....
其实偶数由于处理了2所以全部变成了:
1 ∗ 1 ∗ 3 ∗ 1 ∗ 5 ∗ 3 ∗ 7 ∗ 1 ∗ 9 ∗ 5 ∗ 1 ∗ 3 ∗ 3 ∗ 7..... 1*1*3*1*5*3*7*1*9*5*1*3*3*7..... 11315371951337.....
就可以除以2变为 f ( n / 2 , x ) f(n/2,x) f(n/2,x)
那么奇数我们可以单独写一个函数来处理:
g ( n , x ) g(n,x) g(n,x):n!中奇数位出现x的次数
那么 g ( n , x ) = n / 10 + ( n % 10 > = x ) + g ( n / 5 , x ) g(n,x)= n/10+(n \%10 >= x) +g(n/5,x) g(n,x)=n/10+(n%10>=x)+g(n/5,x)
我们再来看看
1 ∗ 3 ∗ 5 ∗ 7 ∗ 9 ∗ 11 ∗ 13 ∗ 15..... 1*3*5*7*9*11*13*15..... 13579111315.....
那么由于处理了5所以变成了
1 ∗ 3 ∗ 1 ∗ 7 ∗ 9 ∗ 11 ∗ 13 ∗ 3..... 1*3*1*7*9*11*13*3..... 1317911133.....
那么 f ( n , x ) = f ( n / 2 , x ) + g ( n , x ) f(n,x)=f(n/2,x)+g(n,x) f(n,x)=f(n/2,x)+g(n,x)
在注意一下,如果 n u m 2 > = n u m 5 num2>=num5 num2>=num5要特殊处理

#include<set>
#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<vector>
#include<climits>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
#define ULL unsigned long long
int read(){
	int f=1,x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return f*x;
}
#define MAXN 10
#define INF 0x3f3f3f3f
int table[4][4]={//2,3,7,9
{6,2,4,8},
{1,3,9,7},
{1,7,9,3},
{1,9,1,9}
};
int get2(int n){
	if(n==0) return 0;
	return n/2+get2(n/2);
}
int get5(int n){
	if(n==0) return 0;
	return n/5+get5(n/5);
}
int getnum(int n,int x){//3,7,9
	if(n==0) return 0;
	return n/10+(n%10>=x)+getnum(n/5,x);
}
int cal(int n,int x){
	if(n==0) return 0;
	return cal(n/2,x)+getnum(n,x);
}
int main(){
	int n,m;
	while(~scanf("%d %d",&n,&m)){
		m=n-m;
		int ans=1;
		int num2=get2(n)-get2(m);
		int num5=get5(n)-get5(m);
		int num3=cal(n,3)-cal(m,3);
		int num7=cal(n,7)-cal(m,7);
		int num9=cal(n,9)-cal(m,9);
		//printf("%d %d %d %d %d\n",num2,num5,num3,num7,num9);
		if(num5>num2){
			puts("5");
			continue;
		}
		if(num2>num5)
			ans*=table[0][(num2-num5)%4],ans%=10;
		ans*=table[1][num3%4],ans%=10;
		ans*=table[2][num7%4],ans%=10;
		ans*=table[3][num9%4],ans%=10;
		printf("%d\n",ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值