I(1021): 组合数末尾的零

                                I(1021): 组合数末尾的零

Description

m个不同元素中取出n (n m)个元素的所有组合的个数,叫做从m个不同元素中取出n个元素的组合数。组合数的计算公式如下:

C(m, n) = m!/((m - n)!n!) 

现在请问,如果将组合数C(m, n)写成二进制数,请问转这个二进制数末尾有多少个零。

Input

第一行是测试样例的个数T,接下来是T个测试样例,每个测试样例占一行,有两个数,依次是mn,其中m ≤ 1000。

Output

分别输出每一个组合数转换成二进制数后末尾零的数量。

Sample Input

24 21000 500

Sample Output

16
很显然并不能直接求阶乘,因为数太大了,我估计long double都不一定存的下。
那怎么办呢。首先我们要知道一件事,一个数的二进制末尾有多少个0,那这个数就是二的多少次方的倍数。就好像24是8的倍数,所以24转成二进制后末尾一定是三个0.
至于说为什么,我是这样想的,我们知道所有的奇数的末尾都是1,偶数的末尾都是0,即是二的倍数。暂时先不考虑奇数的话,我们将一个数的二进制数的末尾删掉一个0,那么这个数会减半,如果这里不能理解的话可以回想一下C语言的按位右移,右移一位就除二,两位就除4。这样,如果一个数是4的倍数,那它在除2之后肯定还可以再被二除一次,也就是说它的末尾肯定还会有一个0.同理,三个0就是8的倍数,四个0就是16的倍数。

但这样还不够,还是无法解题。然后我们要知道的就是,两个数的二进制数相乘(或相除),它们末尾的0的个数不会改变,就像4*6=24,4的末尾两个0,6的末尾一个0,所以24的末尾有三个0.关于这个的理由可以联系上一段0的个数的原因。还是拿4*6举例。4是4的倍数,6是2的倍数(因为6不是2的多少次方,所以不能说6是6的倍数),所以24既是4的倍数又是2的倍数,也就是8的倍数,所以是三个0.

到了这里终于离结果不远了。直接举例吧,1000 和 500 的。组合数的结果就是1000! / (500! * 500!) 。我们不用知道1000!是多少,也几乎不可能知道,但我们可以知道1000!的二进制数末尾有多少个0.记住除是减,乘是加就好了。下面是代码。如果有疑问欢迎提出。

#include<iostream>
#include<cstring>
using namespace std;
int a[1005], b[1005];
int main()
{
	memset(a, 0, sizeof(a));
	memset(b, 0, sizeof(b));
	for (int i = 1; i < 1003; i++)
	{
		int j = i;
		int num = 0;
		while (j % 2 == 0 && j)
		{
			num++;//计算0的个数
			j >>= 1;//按位右移,即除以2
		}
		b[i] = num;//i的二进制数末尾0的个数
		a[i] = a[i - 1] + num;//i的阶乘的末尾的0的个数
		num = 0;
	}
	int x, y;
	int t;
	cin >> t;
	while (t--)
	{
		cin >> x >> y;
		cout << a[x] - a[y] - a[x-y] <<endl;
	}
	return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值