zoukankan      html  css  js  c++  java
  • POJ 1150 The Last Nonzero Digit

    The Last Non-zero Digit
    Time Limit: 1000MS   Memory Limit: 65536K
    Total Submissions: 4259   Accepted: 1205

    Description

    In this problem you will be given two decimal integer number N, M. You will have to find the last non-zero digit of the NPM.This means no of permutations of N things taking M at a time.

    Input

    The input contains several lines of input. Each line of the input file contains two integers N (0 <= N<= 20000000), M (0 <= M <= N).

    Output

    For each line of the input you should output a single digit, which is the last non-zero digit of NPM. For example, if NPM is 720 then the last non-zero digit is 2. So in this case your output should be 2.

    Sample Input

    10 10
    10 5
    25 6

    Sample Output

    8
    4
    2

    Source

    这个题怎么来做呢?先别急,我们先来讨论一下下面几个子问题: 1.如何求出n阶乘中质因数x(比如说5)出现的次数? 比如说15的阶乘 :1*2*3*4*5*6*7*8*9*10*11*12*13*14*15 由于5这个质因数只在5的倍数里面才出现,所以我从5,10,15中各抽出一个5,这相当于有15/5个质因数5,之后5,10,15就变成了1,2,3; 由于非5的倍数显然不在考虑范围之内,所以我们只需继续讨论它的子问题3!即可。 这样,我们可以用递归来解决这个问题。有了这个方法,我们是不是能够轻易地解决n!末尾有多少个0呢?想想看...n!后0的个数是不是就和某个质因数的个数有关呢?^_^ 比如说,我们要求5出现的次数,可以这样写:

    int get5(int n)//计算n!中质因子5的出现次数 {     if(n==0)         return 0;     return n/5+get5(n/5); }

     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,由于2,5因子已经被去除,那么剩下的数字末尾一定是3,7,9,1中四者之一。然后我们再求出这么一串数相乘以后末尾的数是几.最后再补上2对末位的影响即可!
    总结一下,求10!最后一个非0位的步骤如下: step1:首先将10!中所有2,5因子去掉; step2:然后求出剩下的一串数字相乘后末尾的那个数。 step3:由于去掉的2比5多,最后还要考虑多余的那部分2对结果的影响。 step4:output your answer!

    正如上面文章里所说的“To compute the number of 3,7,9 among (f(1) mod 10), (f(2) mod 10), ..., (f(N) mod 10) is not so easy”,这里面步骤2是个难点。如何求出剩下的这串数字相乘后最后一位是几呢?这可以转化成求出这些数里面末尾是3,7,9的数字出现的次数(为啥?因为这些数的n次方是有规律的,周期为4,不信你可以推一下)
    好,现在问题就是如何求出这串数字中末尾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的数的个数了

    好了,现在我们得到了这串数字中末尾是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,所以就不需要特殊考虑了。

    #include<stdio.h>
    #include<string.h>
    #include<math.h>
    
    int get2(int n){    //计算n!中质因子2的出现次数
        return n==0?0:(n/2+get2(n/2));
    }
    
    int get5(int n){    //计算n!中质因子5的出现次数
        return n==0?0:(n/5+get5(n/5));
    }
    
    int g(int n,int x){     //计算f(1) to f(n) 中,奇数数列中末尾为x的数出现的次数
        if(n==0)
            return 0;
        return n/10+(n%10>=x)+g(n/5,x);
    }
    
    int getx(int n,int x){      //计算f(1) to f(n)中,末尾为x的数的出现次数
        if(n==0)
            return 0;
        return getx(n/2,x)+g(n,x);
    }
    
    int table[4][4]={
        {6,2,4,8},  //2^n%10的循环节,注意如果2的个数为0时候,结果应该是1,要特殊处理。
        {1,3,9,7},  //3
        {1,7,9,3},  //7
        {1,9,1,9}   //9
    };//3,7,9的循环节中第一位,刚好是1,故不需要考虑这些数字出现次数为0的情况。
    
    int main(){
    
        //freopen("input.txt","r",stdin);
    
        int n,m;
        while(scanf("%d%d",&n,&m)!=EOF){
            int num2=get2(n)-get2(n-m);
            int num5=get5(n)-get5(n-m);
            int num3=getx(n,3)-getx(n-m,3);
            int num7=getx(n,7)-getx(n-m,7);
            int num9=getx(n,9)-getx(n-m,9);
            int ans=1;
            if(num5>num2){
                printf("5\n");
            }else{
                if(num2!=num5){
                    ans*=table[0][(num2-num5)%4];
                    ans%=10;
                }       //如果num2==num5,那么2^0次方mod 10应该为1 ,而不是table中的6,所以要特殊处理。
                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;
    }
  • 相关阅读:
    Eclipse配置方法注释模板
    彻底清除Github上某个文件以及历史
    eclipse快捷键
    Hibernate执行原生SQL
    API接口规范
    eclipse配置google代码风格
    eclipse format xml
    git撤销commit
    使用postman测试文件上传
    centos7下部署elasticSearch集群
  • 原文地址:https://www.cnblogs.com/jackge/p/2990790.html
Copyright © 2011-2022 走看看