LETTers比赛第四场-Last non-zero Digit in N!

Last non-zero Digit in N!

该题数据较大,n可能有数百位,直接计算n!或通过n次计算都必然会超时。关键在于找出数字的规律。对于n<10,直接枚举即可,下面考虑n>=10的情况。

n!的尾部的0都来自因子5和因子2(一对5&2产生一个0),如果把这些因子去掉,则可符合要求。

定义F(n)为所要求的数,G(n)为1,2…n中将5的倍数的数换成1后的各项乘积(如:G(15)=1*2*3*4*1*6*7*8*9*1*11*12*13*14*1)(G(n)%10必不为0)。

则n!=(n/5)!*5^(n/5)*G(n),F(n)=F(n/5)*[5^(n/5)*G(n)%10],这可用递归处理,另外观察到5^(n/5)*G(n)%10=G(n)/2^(n/5)%10,这里G(n)%10(n>=10)其实是个周期为10的周期序列(程序中的table),G(n)/2^(n/5)必为偶数,这样得到一种特殊除法:8/2=4,4/2=2,2/2=6,6/2=8…,这样G(n)/[2^(n/5)]%10只与G(n)%10和(n/5)%4有关。而G(n)%10=table[n%10],n/5%4=n/5%100%4(%100好算,就是最后两位数字)。

#include<iostream>
#include<string.h>
using namespace std;
#define MAXN 1000
char n[MAXN];
int  b[MAXN];
const int m[10]={1,1,2,6,4,2,2,4,2,8}; // 对小于10的数直接得到非零尾数
const int table[10]={6,6,2,6,4,4,4,8,4,6};
const int even[4]={8,4,2,6};
int div2(int a, int c) // a/(2^c)
{
    int i;
    for(i=0; i<4 && even[i]!=a; i++);
    if(i>=4)
        return 0;
    return even[(i+c)%4];
}
int lastbit(char*s)
{
    int len=strlen(s);
    if(len<2)
        return m[s[0]-'0'];
    int i, c, bit=1;
    for(i=len-1; i>=0; i--)
        b[len-1-i]=s[i]-'0';
    while(len>1)
    {
        int k = b[0];
        for(i=1,c=b[0]/5; i<=len; i++) // b=b/5(b=b*2/10)
        {
            c = b[i] * 2 + c;
            b[i-1] = c % 10;
            c /= 10;
        }
        len -= !b[len-1];
        bit = bit * div2(table[k],b[0]+b[1]*10) % 10;
    }
    bit = bit * m[b[0]] % 10;
    return bit;
}
int main(void)
{
    while(cin>>n)
        cout<<lastbit(n)<<endl;
    return 0;
}
posted @ 2012-04-18 22:32 LETTers 阅读(...) 评论(...) 编辑 收藏