HDOJ1066-数学,N!的非零尾数

3 篇文章 0 订阅
/*
求N!最后非0位的值。比如2是120的最后一个不是0的值。
输入N比较大,要大数保存。
注意到最后0的个数是与5的因数的个数相等。设f(n)为n!的最后非0位。
那么f(n)=((n%5)!* f(n/5) *2^(n/5))%10
因数2的个数始终大于5,从1开始每连续5个划分为1组,其中5的倍数只提取出一个因数5后,
组成一个新的数列1到n/5,我们有1*2*3*4*5=6*7*8*9*5=11*12*13*14*5=...=2(取最后一个非0位),这里就是2^(n/5)。
再乘上剩下来的几个数字即可
(比如n是123,那么第一次会剩下121,122,123三个数没有被分配)。
参考: http://hi.baidu.com/nicker2010/item/4fa83c4c5050b3e5a4c066ec  代码三
*/
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

const int Lib[4]={6,2,4,8};  //2^n的尾数为2,4,8,6的循环
const int fact[10]={1,1,2,6,4,2,2,4,2,8};  //10以内的阶乘尾数

char s[200];
int a[200];  //存大数,a[0]存数的位数

void todigit(char s[],int a[])
{
    a[2]=0;
    a[0]=strlen(s);
    for (int i=0; i<a[0]; i++) a[a[0]-i]=s[i]-'0';
}

void mult(int a[],int x) //高精度除法
{  
    int j=0;
    for (int i=a[0]; i>0; i--)
    {
        int k1=(j*10+a[i])/x;
        j=(j*10+a[i])%x;
        a[i]=k1;
    }
    while (a[0]>1 && a[a[0]]==0) a[0]--;
}

int last_nunzero(int a[])
{
    if (a[0]==1) return fact[a[1]];
    int x1=fact[a[1]%5];                //x1=(n%5)!
    mult(a,5);
    int x2=Lib[(a[2]*10+a[1])%4];       //x2=(2^(n/5))%10
    int ret=(x1*x2*last_nunzero(a))%10; //递推公式的还原
    return ret;
}

int main()
{
    while (gets(s))
    {
        todigit(s,a);
        printf("%d\n",last_nunzero(a));
    }
    return 0;
}

/*
网路上的另一个更高效的模板: (思路不甚理解,同参考网址的代码四(代码四本身是WA的~),)
#include <stdio.h>
#include <string.h>
#define MAXN 10000

int lastdigit(char* buf){
 const int mod[20]={1,1,2,6,4,2,2,4,2,8,4,4,8,4,6,8,8,6,8,2};
 int len=strlen(buf),a[MAXN],i,c,ret=1;
 if (len==1)
 return mod[buf[0]-'0'];
 for (i=0;i<len;i++)
  a[i]=buf[len-1-i]-'0';
 for (;len;len-=!a[len-1]){
  ret=ret*mod[a[1]%2*10+a[0]]%5;
  for (c=0,i=len-1;i>=0;i--)
   c=c*10+a[i],a[i]=c/5,c%=5;
 }
 return ret+ret%2*5;
}

int main()
{
 char en[1000];
 while(scanf("%s",en)!=EOF)
 {
  int temp = lastdigit(en);
  printf("%d\n",temp);
 }
 return 0;
}
*/

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值