JDOJ 1775: 求N!中0的个数

JDOJ 1775: 求N!中0的个数

JDOJ传送门

Description

求N!结果中末尾0的个数

N! = 1 * 2 * 3 ....... N

Input

输入一行,N(0 < N < unsigned INT_MAX)

Output

输出一行,0的个数

Sample Input

5

Sample Output

1

题解:

\(\prod_{i=1}^{i=n}\)中末尾0的个数,其实就是在求中\(\prod_{i=1}^{i=n}\)能被几个10整除。

因为\(\prod_{i=1}^{i=n}\)是连乘,所以想要乘出来一个10,那么当且仅当一个2和一个5相乘。

我们随便脑补一下,都会发现,\(\prod_{i=1}^{i=n}\)中出现2的几率一定比出现5的几率大得多,也就是说,如果\(\prod_{i=1}^{i=n}\)中有\(n\)个2,\(m\)个5,那么不一定有\(n\)个10,但一定会有\(m\)个10.

所以原题就变成了求\(\prod_{i=1}^{i=n}\)中能拆分出几个5。

得出第一份代码:

#include<cstdio>
#define ll long long
using namespace std;
ll n,ans;
int main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        int j=i;
        while(j%5==0)
        {
            ans++;
            j/=5;
        }
    }
    printf("%lld",ans);
    return 0;
}

正确性可以保证,但是会TLE。

原因是这道题的数据很大。如果这样从1跑到\(N\),再一个个拆分,就一定会爆时间。那么我们开始往优化算法的方面去想。我们发现,一次跑1个5总不会有1次跑很多个5省事。没错,优化暴力枚举的大多数方法都是在原暴力的基础上合并可以一次筛选出来的东西,一次性加一起。

基于这个思想,我们发现,一个数中能拆出多少个5,其实就是能否拆出一个\(5^n\),所以我们只需要枚举判断能否拆出\(5^n\)即可。这样的运行效率会快非常多。

代码:

#include<cstdio>
#include<cmath>
#define ll long long
using namespace std;
ll n,ans,temp;
int main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        if(n/pow(5,i)==0)
            break;
        temp=n/pow(5,i);
        ans+=temp;
    }
    printf("%lld",ans);
    return 0;
}
posted @ 2019-09-03 19:49  Seaway-Fu  阅读(282)  评论(2编辑  收藏  举报