争取先将每个题型先熟悉一边,今天就发一个数论的题目——计算阶乘末尾第一个非0数字。这是zoj上的一道题目有兴趣的可以去看下链接为http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1222。
思路分析:
1、N非常大的时候, 如果算阶乘的时候跳过5的倍数,记G(n)为跳过5的倍数的时候, 从1乘到n的最末非零位,也就是把5的倍数当1乘。可以发现:G(1) = 1, G(2) = 2, G(3) = 6, G(4) = 4, G(5) = 4, G(6) = 4, G(7) = 8, G(8) = 4, G(9) = 6, G(10) = 6, G(11) = 6, G(12) = 2, G(13) = 6... 又出现了循环,每10个数循环一次。所以这里出现了一个循环节:6 2 6 4 4 4 8 4 6 6 (0,1的时候是特例,单独考虑。)这个表示从0乘到9(将5换为1)的非0尾数。
2、下面考虑5的倍数:5 10 15 20 25 30 35 40 ........... 把5提取之后
1 2 3 4 5 6 7 。。。 n/5
可以发现又是一个关于f(n/5)的式子 最终: f(n) = (f(n/5) * 5^(n/5))* g(n) ; 其中f(n)为n!的最后非零位,g(n)为排除5的倍数时的最后非零位,(f(n/5) * 5^(n/5)) 所有5的倍数乘积的最后非零位。 复杂log(n),稍微处理一下大数。
代码如下:
#include<string.h> #include<cstdio> #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']; //特殊情况当数小于10 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 a[10000]; while(scanf("%s",a)!=EOF) { printf("%d\n",lastdigit(a)); } return 0; }