N!的从最末一个非0位开始自低位向高位数的第M位 soj 1115

                                     阶乘

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 ;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值