求任意数阶乘最后一位

今天看完了离散关于求模的那一章然后顿时想到了这个的解法…ACM大神勿喷…(我不是竞赛的……)

/*
    Author: Latyas
            Jilin University

            思路:防止5的影响,将n!分解为2^a*5^b*c1c2c3c4...cn
*/
#include <stdio.h>
#include <stdlib.h>

int TWO = 0;
int FIVE = 0;

int Run(int);
int fuckTwoFive(int); 
int CongruenceExp(int base,int exp,int m); //同余幂算法

int main()
{
    int temp = 1;

    while(scanf("%d",&temp))
    {
        printf("%d -> %d\n",temp,Run(temp));
        TWO  = FIVE  = 0;
    }

    system("pause");
    return 0;
}
int Run(int input)
{
    if(input == 1 || input == 0) return 1;

    int result = 1;

    for(int i = 1;i<=input;i++)
    {
        result *= fuckTwoFive(i)%10;    
        //根据求模公式的变形防止数值膨胀
        result %= 10;
    }

    TWO -= FIVE;

    /*
        算2的n次幂模10的结果有两个方法
        1.2^n % 10的结果是循环出现的,注释掉的代码即这种方法,有特殊性
        2.更一般的算法是2^t 将t展开为二进制,即用同余幂算法求模
          这里用这个方法,因为代码看上去更简洁了 =w=
    */
    int Tworesult = 1;
    /*
    static const int map2[5] = {1,2,4,8,6};
    

    for(int i = 0;i<=TWO/4;i++)
    {
        if((Tworesult *= 6) > 10) Tworesult %= 10;
    }

    Tworesult = (Tworesult*map2[TWO%4])%10;
    
    */
    Tworesult = CongruenceExp(2,TWO,10); //同余幂求模
    result = (result*Tworesult)%10;
    return result;
}
int fuckTwoFive(int input)
{
    if(input%5 == 0)
    {
        while(1)
        {
            if(input%5 != 0) break;
            input /= 5;
            FIVE++;
        }
    }

    if(input%2 == 0)
    {
        while(1)
        {
            if(input%2 != 0) break;
            input /= 2;
            TWO++;
        }
    }

    return input;
}
int CongruenceExp(int base,int exp,int m)
{
    int k = 0;
    int temp = exp;
    for(int i = 0 ;;i++)
    {
        if((temp/=2) == 0) 
        {
            k = i;
            break;
        }
    }
    int x = 1;
    int power = base%m;
    for(int i = 0;i<=k;i++)
    {
        if(exp&(1<<i))
        {
            x = (x*power)%m;
        }
        power = (power*power)%m;
    }
    return x;

}
posted @ 2012-08-01 01:55  latyas  阅读(325)  评论(0编辑  收藏  举报