您的位置:首页 > 其它

bzoj 3552: 最右非零的数 && hduoj 1066: Last non-zero Digit in N!(求N!的最后一个非0位)

2016-06-29 19:18 260 查看

3552: 最右非零的数

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 89  Solved: 27

[Submit][Status][Discuss]

Description

       给出正整数N(可能有前导0),请求出N!最右非零的数位的值。

Input

       第一行一个数T表示数据组数
       下接T行每行一个数N表示一组数据

Output

       对于每组数据,输出一行一个数表示这组数据的答案

Sample Input

2

5

4

Sample Output

2

4

思路与公式:

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

明显多于5的个数)

2:设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)!) * F[5^(n/5) * G(n)] (可以递归)

3:根据②可知F[5^(n/5) * G(n)] = F[G(n)/(2^(n/5))],其中G(n)/(2^(n/5))可找到规律

4:枚举可找到上述规律为Mp[20] = {1,1,2,6,4,2,2,4,2,8,4,4,8,4,6,8,8,6,8,2}(20一循环)这样就可以算出答案啦

PS:

n<=19  ----  G
= data

n>=20  ----  G
= data[n%20]

n过大,这里用字符串处理

#include<stdio.h>
#include<string.h>
char str[10005];
int a[10005], Mp[20] = {1,1,2,6,4,2,2,4,2,8,4,4,8,4,6,8,8,6,8,2}; /*这个是F[5^(n/5)*G(n)]的前20(0-19)项,后面循环*/
int main(void)
{
int T, len, ret, i, c;
scanf("%d", &T);
while(T--)
{
scanf("%s", str);
len = strlen(str);
for(i=len-1;i>=0;i--)
a[len-1-i] = str[i]-'0'; /*将n的每一位存入数组a[],其中a[0]是最低位,a[len-1]是最高位*/
ret = 1;
while(len!=0) /*用循环代替递归*/
{
c = 0;
ret = ret*Mp[a[1]%2*10+a[0]]%10; /*"a[1]%2*10+a[0]"是计算n%20的值*/
for(i=len-1;i>=0;i--)
{
c = c*10+a[i]; /*计算n除以5之后的值,并将其存入(覆盖)a[]*/
a[i] = c/5;
c %= 5;
}
if(a[len-1]==0) /*如果最高位是0,去掉最高位*/
len--;
}
if(strcmp(str, "1")==0 || strcmp(str, "0")==0)
printf("1\n");
else
printf("%d\n", ret);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: