poj1150

题意:

计算A(n,m)的最后一位非0的数。



分析引用下面文章。出处:http://duanple.blog.163.com/blog/static/7097176720081016113033592/

http://acm.pku.edu.cn/JudgeOnline/problem?id=1150

最最原始的问题,是那个计算n!的结果中的0的个数的问题,对于这个问题的解答是,我们只需要观察1*2*3...n的因子中,因子5的个数就可以了。因为末尾0的个数,实际上反映了结果中因子10的个数,而10=2*5,由于在1*2*3...n中,2的个数始终是大于5的,所以10的个数实际上是由因子5的个数决定的,所以需要计算出5的个数就好了。

那怎么计算因子5的个数呢?

是利用了递归,f(n) = n/5 + f(n/5)

可以这样理解,对于1 2 3 4 5 6 7 8 9 10 .....

首先可以看到含有因子5的数必然是5的整数倍,5,10,15,20,25

我们首先从这些数抽取出一个5,可以抽取出的5的个数就是n/5,而这些数抽取出一个5后,变成了1,2,3,4....n/5

所以变成了一个子问题,故递归可解。

 

进一步的计算 n!的最后一个非0位

这个问题要比上一个问题难,但是依旧要利用递归,以及2,5

对于这样的一系列数的乘法,它的最后一位是怎么形成的呢,首先2*5组成了那些0位,所以相同数目的2,5对最后一个非0位没有影响,所以我们可以把2,5分解掉,但是有时候2的个数是大于5的,所以我们还要记住剩下的2的个数,因为它是对最后结果有影响的。然后我们需要观察其他数的最后一位,因为这些最后一位对最终n!的结果形成了影响。

以1*2*3*4*5*6*7*8*9*10为例

第一步,我们需要把这些数里,因子2和5分解出去,变成

1*1*3*1*1*3*7*1*9*1

(容易看出,当对于任意的一个数x,当因子2,和5分解出去后,它的最后一位必然是1,3,7,9中的某一个)

所以我们只要能够统计出这个结果中,1,3,7,9的个数就能最终求出n!的最后一位了

所以关键在于统计末尾为1,3,7,9的个数,这个统计的方法就需要我们继续观察了。

第二步,统计除去因子2,5后末尾为1,3,7,9的个数

一个数列实际上可以分成偶数列和奇数列,以1*2*3*4*5*6*7*8*9*10为例

分成1 3 5 7 9,   2 4 6 8 10

这样我们尝试分别进行统计,可以发现,实际上2,4,6,8,10中的个数也就是1 2 3 4 5中的个数,也就是说我们又把这个问题划分成了一个原来问题的子问题。

f(n) = f(n/2) + g(n),g(n)表示奇数列中的数目,所以我们需要解决g(n)

再次观察g(n)

实际上又分成了两部分1 3 7 9 11 13 17 19 21。。。以及5的奇倍数5,15,25。。。说明又出现了子问题,如果要统计这个数列中末尾为x(1,3,7,9)的个数可以这样写:g(n,x) = n/10+(n%10 >= x)+g(n/5,x) 

这样利用了两个递归方程,我们就可以在lgn的时间内计算出末尾为1,3,7,9的数的个数了

最终,我们还要加上剩余的2来共同决定最后的一个非0位,而对于1,2,3,7,9他们的乘法的最后一位也是有循环节的,以2为例,连续的2相承,最后一位是这样的一个循环2,4,8,6,2,4,8,6........

这样我们就得到了计算n!最后一个非0位的方法。

计算n!/(n-m)!的最后一个非零位

因为(n-m)!是n!的一个子集,所以我们只有分别计算出它们中2,3,7,9的个数,然后用n!的减去(n-m)!的,就是最终n*n-1*。。。n-m+1的。也就是poj1150的解法。

计算n!/(m!*(n-m)!)也就是C(n,m)的最后一个非零位----poj3406

因为(m!*(n-m)!)不是n!的一个子集,所以这个不能利用1150的方法,而且观察它的input,实际上只有一个case,而且n ,m< 1000000,所以nlogn的方法是可以过的。

但是我们可以采用最简单的思路来解它,也就是直接遍历,对于每个数,计算2,5因子数,以及把它的因子2,5去除剩余的最后一位,最后把这些最后一位相乘,再求最后一位再考虑多余的2.5,然后求出最后一位。思路一样,但是求解2,5,3,7,9的方法采用了遍历的方法,而不是递归了。。。


代码:


#include <iostream>
#include <cmath>
#include <stdio.h>
#include <map>
#include <algorithm>
using namespace std;
#define LL long long 
#define MAX 10010

int get2(int n)
{
	if(!n) return 0;
	return n/2+get2(n/2);
}
int get5(int n)
{
	if (!n) return 0;
	return n/5+get5(n/5);
}
int getox(int n,int x)
{
	if(!n) return 0;
	return n/10+(n%10>=x)+getox(n/5,x);
}
int getx(int n,int x)
{
	if(!n) return 0;
	return getx(n/2,x)+getox(n,x);
}

int table[4][4]
={
	6,2,4,8,
	1,3,9,7,
	1,7,9,3,
	1,9,1,9
};

int main()
{
	int m,n;
	while (~scanf("%d%d",&n,&m))
	{
		int ans=1;
		int num2=get2(n)-get2(n-m);
		int num3=getx(n,3)-getx(n-m,3);
		int num5=get5(n)-get5(n-m);
		int num7=getx(n,7)-getx(n-m,7);
		int num9=getx(n,9)-getx(n-m,9);
		if(num2<num5)
		{
			puts("5\n");
			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);

	}
	
}


你可能感兴趣的:(poj1150)