阶乘
N的阶乘定义为:N!=N×(N-1)×……×2×1
请编写一个程序,输出N的阶乘的十进制表示中从最末一个非0位开始自低位向高位数的第M位。
其中:0<=N<=10000,1<=K<=5
例如:N=5,M=2,结果是1(5!=120) N=8,M=3,结果为0(8!=40320)
输入:
第一行一个整数M (1<=M<=100),代表测试数据的个数;
接下来M行,每行两个整数N,K
输出:
输出M行,每行一个整数,即测试数据的结果。
样例:
输入:
2
5 2
8 3
输出:
1
0
题意:很明了,就是求n!十进制表示中的从最末一个非0位开始自低位向高位数的第M位。有种思路就是将n!求出来,直接计算。
这种方法耗时828ms,memory 692K。
#include < iostream >
#include < string .h >
#include < stdio.h >
#define MOD 10000
using namespace std;
int main( void )
{
int t;
scanf( " %d " , & t);
while (t -- )
{
int i,j;
int n,k;
int digit,carry; //digit表示当前计算的值的位数,carry表示进位
int f[ 10000 ];
char s1[ 50000 ] = { ' \0 ' };
char s2[ 5 ];
long long temp;
f[ 0 ] = 1 ;
digit = 1 ;
scanf( " %d%d " , & n, & k);
for (i = 2 ;i <= n;i ++ ) //求n!
{
carry = 0 ;
for (j = 0 ;j < digit;j ++ )
{
temp = ( long long )(f[j]) * ( long long )(i) + carry; //此处要用64位数据,防止数据溢出
f[j] = temp % MOD;
carry = temp / MOD;
}
while (carry)
{
f[digit ++ ] = carry % MOD;
carry = carry / MOD;
}
}
i = digit - 1 ;
while (f[i] == 0 )
{
i -- ;
}
sprintf(s2, " %d " ,f[i]);
strcat(s1,s2);
i -- ;
for (;i >= 0 ;i -- ) //将结果存进字符数组中以便于处理
{
sprintf(s2, " %04d " ,f[i]); //不足4位前面补0
strcat(s1,s2);
}
for (i = strlen(s1) - 1 ;i >= 0 ;i -- )
{
if (s1[i] != ' 0 ' )
break ;
}
printf( " %d\n " ,s1[i - k + 1 ] - 48 );
}
return 0 ;
}
下面是另外一种思路
由于是求最末一个非0位开始自低位向高位数的第M位,那么末尾的0则可以忽略掉,并且最多只需保存5位结果即可(M<=5)。
如:n!=A1A2A3..........Ai000000000 Ai!=0
则只需取A(i+4)A(i+3)A(i+2)A(i+1)Ai这5位即可.
耗时0ms,memory 580K,明显效率高很多。
#include < iostream >
#define MOD 100000
using namespace std;
int main( void )
{
int t;
scanf( " %d " , & t);
while (t -- )
{
int n,k;
int i,ans;
ans = 1 ;
scanf( " %d %d " , & n, & k);
for (i = n;i >= 2 ;i -- )
{
ans *= i;
while (ans % 10 == 0 ) // 消除末尾的0
{
ans /= 10 ;
}
if (ans >= MOD) // 只需保存末尾5位数字即可
ans %= MOD;
}
for (i = 1 ;i < k;i ++ )
{
ans = ans / 10 ;
}
ans = ans % 10 ;
printf( " %d\n " ,ans);
}
return 0 ;
}