poj 1150 The Last Non-zero Digit

转载:http://blog.csdn.net/luojinping/article/details/7555293

1.如何求出n阶乘中质因数x(比如说5)出现的次数?

  1. int get(int m, int n)//计算n!中质因子m的出现次数
  2. {
  3. if (n == 0) return 0;
  4. return (n / m) + get(m, n / m);
  5. }

解析:

求n!中m出现的次数。那么n可以分解为

 


即为求q的值。

例如:假设n=10,m=3; n! = 1*2*3*4*5*6*7*8*9*10那么出现质因数3的次数为4次。

有公式:


[n/m]表示不大于n的数中m的倍数贡献一个m[n/] 表示不大于n的数中的倍数再贡献一个m

例如:

n=25,m=3;

[n/m] = 8 代表:1乘到25中出现了8次3的倍数,他们分别是:3,6,9,12,15,18,21,24。

[n/] = 2 代表:1乘到25中出现了2次的倍数,他们分别是:9,18。

由于之前9和18在3的倍数中已经贡献了一个3出去了。现在在的倍数中再次出现的时候,只需要再贡献一个3(也是最后一个3)出去即可。如果有一个数是m的5次方。那么在这个数会在[n/]的时候贡献出最后一个3。

 2.如何求出n!阶乘最后非0位?
比如说我们要找10!最后非0位,由于质因数2和5组合之后会使得末尾产生0.那么我们不妨把10!中2,5质因数全部去掉,(但是请注意2的数目其实比5的要多,所以我们要在最后考虑多余的2对末位的影响)
如 1*2*3*4*5*6*7*8*9*10 去掉2 ,5 因子后 就是1*1*3*1*1*3*7*1*9*1,由于25因子已经被去除,那么剩下的数字末尾一定是3791中四者之一。然后我们再求出这么一串数相乘以后末尾的数是几.最后再补上2对末位的影响即可!

3.总结一下,求10!最后一个非0位的步骤如下:
step1:首先将10!中所有2,5因子去掉;
step2:然后求出剩下的一串数字相乘后末尾的那个数。
step3:由于去掉的2比5多,最后还要考虑多余的那部分2对结果的影响。
step4:output your answer!

这里面步骤2是个难点。如何求出剩下的这串数字相乘后最后一位是几呢?这可以转化成求出这些数里面末尾是3,7,9的数字出现的次数,然后依次算出3,7,9出现次数次方的尾数。比如,3出现了4次,则3出现的次数产生的尾数就是1。7出现了2次,7出现的次数产生的尾数就是4。这样,最后把3,7,9出现的次数统计出来,然后依次求出这三个数出现次数的次方后的尾数,最后尾数相乘,即得步骤2的结果。注意,因为这些数(3,7,9)的n次方的尾数是有规律的,周期为4,不信你可以推一下。
好,现在问题就是如何求出这串数字中末尾379各自出现的次数了;

一个数列实际上可以分成偶数列和奇数列,以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中的个数,也就是说我们又把这个问题划分成了一个原来问题的子问题。


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

 再次观察g(n)

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

n/10表示有多少组(1,3,7,9)。例如n=36时,n/10=3,表示有3(1,3,7,9),他们是(1,3,7,9)(11,13,17,19)(21,23,27,29)

(n%10 >= x)表示n这个数的个位是否大于x,若是则应表达式为true,遇到前面的加号,自动提升为数值型1。例如,n=36,x=3时,(n%10 >=x) = (6 >= 3) = true

g(n/5,x)表示5的奇倍数51525……的子问题。

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

好了,现在我们得到了这串数字中末尾是3,7,9的数字的个数,我们利用循环节的性质可以快速地算出这串数字相乘后mod 10的结果,在考虑下当时多除的2(其实也可以用循环节来处理),便可求出答案!


解决了上面两个子问题,我想求P(n,m)最后一个非0位就变得十分容易了。
P(n,m)实际上等于 n! / (n-m)!
我们可以求出n! 和(n-m)!中质因数2,5,3,7,9分别出现的次数,然后再各自相减。然后再用循环节处理,即可!
BTW,这里还要注意一个trick,就是2的出现次数如果小于5,(这对于排列数来说是可能的)我们可以直接输出5,如果2的数目等于5,那么2的循环节不需要考虑。至于3,7,9的循环节,由于这些数的4次方末位刚好是1,所以就不需要特殊考虑了。

View Code
View Code 
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<cstring>
#include<vector>
#include<string>
#define LL long long
using namespace std;
int Get_num2( int n )
{
    if( n == 0 ) return 0;
    return n/2 + Get_num2( n/2 );    
}
int Get_num5( int n )
{
    if( n == 0 ) return 0;
    return n/5 + Get_num5( n / 5 );    
}
int Get_odd( int n , int x )
{
    if( n == 0 ) return 0;
    return n/10 + ( n %10 >= x )  + Get_odd( n/5 , x );    
}
int Get_numx( int n , int x )
{
    if( n == 0 ) return 0;
    return Get_numx( n / 2 , x ) + Get_odd( n , x );    
}
int Pow( int n , int x )
{
    int sum = 1;
    for( int i = 0 ; i < n ; i ++ )
         sum *= x;
    return sum;
}
int main(  )
{
    int n,m,num2,num3,num5,num7,num9;
    while( scanf( "%d %d",&n,&m )==2 )
    {
        num2 = Get_num2( n ) - Get_num2( n - m );
        num5 = Get_num5( n ) - Get_num5( n - m );
        if( num5 > num2 ) puts( "5" );
        else
        {
            num3 = (Get_numx( n , 3 ) - Get_numx( n - m , 3 ))%4;
            num7 = (Get_numx( n , 7 ) - Get_numx( n - m , 7 ))%4;
            num9 = (Get_numx( n , 9 ) - Get_numx( n - m , 9 ))%4;
            num2 -= num5;
            if( num2 > 0 ) 
            {
                num2 %= 4;
                if( num2 == 0 ) num2 = 4;
            }    
            int t =Pow(num2,2)*Pow(num3,3)*Pow(num7,7)*Pow(num9,9);
            printf( "%d\n",t%10 );
        }    
    }
    //system( "pause" );
    return 0;
}

 

转载于:https://www.cnblogs.com/bo-tao/archive/2012/08/07/2626087.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值