给出正整数
N
(可能有前导
0
),请求出
N!
最右非零的数位的值。
思路与公式:
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[n] = data[n]
n>=20 ---- G[n] = data[n%20]
n过大,这里用字符串处理
#include
#include
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;
}