求n!,C(n,m)和A(n,m)最后的非零位

求n!,C(n,m),A(n,m)最后的非零位。

先说说怎样求n!最后的非零位吧!
比如找10!最后的非零位,由于质因数2和5的组合之后末尾会产生0,所以我们先把2,5的质因子全部去掉,由于2的数目要比5多,所以我们要在最后考虑
多余的2对末尾的影响。
比如:12345678910去掉2,5的因子后就是:
1
131137191,由于去掉了2,5,那么剩下的数字末尾一定是3,7,9,1四者之一,然后我们再求出这么一串数相乘以后末尾的数是几,最后再补上
2对末位的影响即可。
所以,求n!最后非零位分为4步:
(1)将n!中所有的2,5因子去掉
(2)求出剩下的一串数字相乘后末尾的那个数
(3)由于2比5多,考虑多余的2对结果的影响
(4)输出答案
对于步骤(1),直接计算去除就行了,很简单。
对于步骤(2),是难点,这个问题可以转化为求出这些数里面末尾是3,7,9的数字出现的次数,因为这些数的n次方是有规律的,周期为4.
现在的问题就是如何求出这串数字末尾3,7,9各自出现的次数了。
一个数列实际上可以分为偶数列和奇数列。
例如:123456789*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(x为1,3,7,9)的个数可以这样写:
g(n,x)=n/10+(n%10>=x)+g(n/5,x)
这样利用了两个递归方程,我们可以在logn的时间内计算出末尾为1,3,7,9的数的个数了
在得到这串数字中末尾是3,7,9的数字个数后,再利用循环节的性质可以快速求出这串数字相乘后mod10的结果,再考虑当时多余的2,便可以求出答案。
知道了这个之后,求A(n,m)就不难了。
先求出n!和(n-m)!中2,5,3,7,9分别出现的次数,然后各自相减,再用循环节处理即可。
这里还要注意一下:2的出现次数如果小于5的出现次数,我们可以直接输出5,如果2的出现次数等于5的出现次数,那么2的循环节不需要考虑。至于3,
7,9的循环节,由于这些数的4次方末尾刚好为1,所以不需要特殊考虑

题目:求A(n,m)最后的非零位数字。题目链接:POJ1150
[cpp] view plain copy

#include <iostream>  
  
using namespace std;  
  
int GetCount(int n,int x)    /**计算n!中质因子x出现的次数*/  
{  
    if(n==0) return 0;  
    return n/x+GetCount(n/x,x);  
}  
  
int g(int n,int x)           /**计算f(1)到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)到f(n)中,末尾为x的数的出现次数*/  
{  
    if(n==0) return 0;  
    return getx(n/2,x)+g(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 n,m;  
    int num2,num3,num5,num7,num9;  
    while(cin>>n>>m)  
    {  
        num2=GetCount(n,2)-GetCount(n-m,2);  
        num5=GetCount(n,5)-GetCount(n-m,5);  
        num3=getx(n,3)-getx(n-m,3);  
        num7=getx(n,7)-getx(n-m,7);  
        num9=getx(n,9)-getx(n-m,9);  
        int ans=1;  
        if(num5>num2)  
        {  
            cout<<"5"<<endl;  
            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;  
        cout<<ans<<endl;  
    }  
    return 0;  
}  

题目:求C(n,m)最后的非零位数字。题目链接:POJ3406
由于m的阶乘和n-m的阶乘的乘积并不是n的阶乘的子集,所以这题要多加一个循环模
[cpp] view plain copy

#include <iostream>  
  
using namespace std;  
  
int GetCount(int n,int x)    /**计算n!中质因子x出现的次数*/  
{  
    if(n==0) return 0;  
    return n/x+GetCount(n/x,x);  
}  
  
int g(int n,int x)           /**计算f(1)到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)到f(n)中,末尾为x的数的出现次数*/  
{  
    if(n==0) return 0;  
    return getx(n/2,x)+g(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 n,m;  
    int num2,num3,num5,num7,num9;  
    while(cin>>n>>m)  
    {  
        num2=GetCount(n,2)-GetCount(n-m,2)-GetCount(m,2);  
        num5=GetCount(n,5)-GetCount(n-m,5)-GetCount(m,5);  
        num3=getx(n,3)-getx(n-m,3)-getx(m,3);  
        num7=getx(n,7)-getx(n-m,7)-getx(m,7);  
        num9=getx(n,9)-getx(n-m,9)-getx(m,9);  
        int ans=1;  
        if(num5>num2)  
        {  
            cout<<"5"<<endl;  
            continue;  
        }  
        if(num2!=num5)  
        {  
            ans*=table[0][(num2-num5)%4];  
            ans%=10;  
        }  
        ans*=table[1][(num3%4+4)%4];  
        ans%=10;  
        ans*=table[2][(num7%4+4)%4];  
        ans%=10;  
        ans*=table[3][(num9%4+4)%4];  
        ans%=10;  
        cout<<ans<<endl;  
    }  
    return 0;  
}  
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值