求n!,C(n,m),A(n,m)最后的非零位。
先说说怎样求n!最后的非零位吧!
比如找10!最后的非零位,由于质因数2和5的组合之后末尾会产生0,所以我们先把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对末位的影响即可。
所以,求n!最后非零位分为4步:
(1)将n!中所有的2,5因子去掉
(2)求出剩下的一串数字相乘后末尾的那个数
(3)由于2比5多,考虑多余的2对结果的影响
(4)输出答案
对于步骤(1),直接计算去除就行了,很简单。
对于步骤(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(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
#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的阶乘的子集,所以这题要多加一个循环模
#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;
}