I(1021): 组合数末尾的零
Description
从m个不同元素中取出n (n ≤ m)个元素的所有组合的个数,叫做从m个不同元素中取出n个元素的组合数。组合数的计算公式如下:
C(m, n) = m!/((m - n)!n!)
现在请问,如果将组合数C(m, n)写成二进制数,请问转这个二进制数末尾有多少个零。
Input
第一行是测试样例的个数T,接下来是T个测试样例,每个测试样例占一行,有两个数,依次是m和n,其中n ≤ m ≤ 1000。
Output
分别输出每一个组合数转换成二进制数后末尾零的数量。
Sample Input
24 21000 500
Sample Output
16
那怎么办呢。首先我们要知道一件事,一个数的二进制末尾有多少个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;
}