Last non-zero Digit in N!(HDU-1066)

3 篇文章 0 订阅
1 篇文章 0 订阅


1 题意

  求 n ! n! n!最右边的非零数字。
  链接:link


2 思路

  已知 n ! = 1 × 2 × 3 × … × ( n − 2 ) × ( n − 1 ) × n n!=1×2×3×…×(n-2)×(n-1)×n n!=1×2×3××(n2)×(n1)×n
  以 5 5 5为长度对 n ! n! n!划分, ( 1 × 2 × 3 × 4 × 5 ) × ( 6 × 7 × 8 × 9 × 10 ) × ( 11 × 12 × 13 × 14 × 15 ) × … × r (1 \times 2 \times 3 \times 4 \times 5) \times(6 \times 7 \times 8 \times 9 \times 10) \times(11 \times 12 \times 13 \times 14 \times 15) \times \ldots \times r (1×2×3×4×5)×(6×7×8×9×10)×(11×12×13×14×15)××r
  每一块去掉一个 2 2 2和一个 5 5 5,对结果没有影响。
( 1 × 2 × 3 × 4 × 1 ) 2 × ( 6 × 7 × 8 × 9 × 2 ) 2 × ( 11 × 12 × 13 × 14 × 3 ) 2 × … × r \frac{(1 \times 2 \times 3 \times 4 \times 1)}{2} \times \frac{(6 \times 7 \times 8 \times 9 \times 2)}{2} \times \frac{(11 \times 12 \times 13 \times 14 \times 3)}{2} \times \ldots \times r 2(1×2×3×4×1)×2(6×7×8×9×2)×2(11×12×13×14×3)××r
  取出每一组的最后一个数字。
⌊ n 5 ⌋ ! × ( 1 × 2 × 3 × 4 ) 2 × ( 6 × 7 × 8 × 9 ) 2 × ( 11 × 12 × 13 × 14 ) 2 × … × r \lfloor\frac{n}{5}\rfloor ! \times \frac{(1 \times 2 \times 3 \times 4)}{2} \times \frac{(6 \times 7 \times 8 \times 9)}{2} \times \frac{(11 \times 12 \times 13 \times 14)}{2} \times \ldots \times \mathrm{r} 5n!×2(1×2×3×4)×2(6×7×8×9)×2(11×12×13×14)××r
  此时,每一组对 10 10 10取余都等于 2 2 2,可将上式化简。
⌊ n 5 ⌋ ! × 2 ⌊ n 5 ⌋ × r \lfloor\frac{n}{5}\rfloor ! \times 2^{\lfloor\frac{n}{5}\rfloor} \times r 5n!×25n×r
  每一组数中只有最后一个数能被 5 5 5整除,所以 r r r一定不能被 10 10 10整除,可以直接对其取余。
  由于 n % 10 = 5 n\%10=5 n%10=5 n % 10 = 0 n\%10=0 n%10=0没有余项,余项值暂定为 1 1 1 r % 10 r\%10 r%10 10 10 10为循环节, n % 10 n\%10 n%10 r % 10 r\%10 r%10的关系如下表所示。

n % 10 n\%10 n%10 r % 10 r\%10 r%10
01
11
22
36
44
51
66
72
86
94

   2 n % 10 2^{n}\%10 2n%10 4 4 4为循环节, n ≥ 1 n≥1 n1 n % 4 n\%4 n%4 2 n % 10 2^{n}\%10 2n%10的关系如下表所示。

n % 4 n\%4 n%4 2 n % 10 2^{n}\%10 2n%10
06
12
24
36

  因为 r % 10 r\%10 r%10 10 10 10位循环节, 2 n % 10 2^{n}\%10 2n%10 4 4 4为循环节,所以 ( r × 2 n ) % 10 (r \times 2^{n})\%10 (r×2n)%10 20 20 20,其中 n ≥ 1 n≥1 n1 n % 20 n\%20 n%20 ( r × 2 n ) % 10 (r \times 2^{n})\%10 (r×2n)%10的关系如下表所示。

n % 20 n\%20 n%20 ( r × 2 n ) % 10 (r \times 2^{n})\%10 (r×2n)%10
01
11
22
36
44
52
62
74
82
98
104
114
128
134
146
158
168
176
188
192

  当 n = 0 n=0 n=0 ( r × 2 n ) % 10 = 1 (r \times 2^{n})\%10=1 (r×2n)%10=1,也满足上述表格。
  事先将 ( r × 2 n ) % 10 (r \times 2^{n})\%10 (r×2n)%10 n % 20 n\%20 n%20的映射值存储在 m [ ] m[] m[]数组中,则 L N D ( n ) = L N D ( ⌊ n 5 ⌋ ) × m [ n % 20 ] % 10 \mathrm{LND}(\mathrm{n})=\mathrm{LND}\left(\lfloor\frac{n}{5}\rfloor\right) \times m[\mathrm{n} \% 20] \% 10 LND(n)=LND(5n)×m[n%20]%10

2.1 时间复杂度分析

  根据递推式可知,递归或循环求解只需要 O ( log ⁡ ( n ) ) \mathcal{O}(\log(n)) O(log(n))的时间,由于除 5 5 5也需要 O ( log ⁡ ( n ) ) \mathcal{O}(\log(n)) O(log(n))的时间,所以时间复杂度为 O ( log ⁡ 2 ( n ) ) \mathcal{O}(\log^{2}(n)) O(log2(n))

2.2 实现

2.2.1 递归实现

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e3+10;
int m[20]={1,1,2,6,4,2,2,4,2,8,4,4,8,4,6,8,8,6,8,2};
char buf[N],bufLen;
int num[N],numLen;
int lastdigit(){
    if(numLen==0) return 1;
    int res=m[num[1]%2*10+num[0]]%10;//对20取余
    for(int c=0,i=numLen-1;i>=0;i--){
        c=c*10+num[i],num[i]=c/5,c%=5;//除5
    }
    if(!num[numLen-1]) numLen--;
    return lastdigit()*res%10;
}
int main(){
    while(~scanf("%s",buf)){
        bufLen=strlen(buf);
        for(int i=0;i<bufLen;i++) num[i]=buf[bufLen-1-i]-'0';
        numLen=bufLen;
        printf("%d\n",lastdigit());
    }
    return 0;
}

2.2.2 非递归实现

#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e5+10;
char str[N],a[N];
int m[20]={1,1,2,6,4,2,2,4,2,8,4,4,8,4,6,8,8,6,8,2};
int lastdigit(char *buf){
    int len=strlen(buf),ret=1;
    if(len==1) return m[buf[0]-'0'];
    for(int i=0;i<len;i++) a[i]=buf[len-1-i]-'0';
    for(;len;len-=!a[len-1]){
        ret=ret*m[a[1]%2*10+a[0]]%5;//对20取余
        for(int c=0,i=len-1;i>=0;i--){
            c=c*10+a[i],a[i]=c/5,c%=5;//除5
        }
    }
    return ret+ret%2*5;
}
int main(){
    while(~scanf("%s",str)){
        printf("%d\n",lastdigit(str));
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值