A Trivial Problem(暴力规律 || 数学规律)

26 篇文章 2 订阅

题目:

Mr. Santa asks all the great programmers of the world to solve a trivial problem. He gives them an integer m and asks for the number of positive integers n, such that the factorial of n ends with exactly m zeroes. Are you among those great programmers who can solve this problem?

Input

The only line of input contains an integer m (1 ≤ m ≤ 100 000) — the required number of trailing zeroes in factorial.

Output

First print k — the number of values of n such that the factorial of n ends with m zeroes. Then print these k integers in increasing order.

Examples

Input

1

Output

5
5 6 7 8 9 

Input

5

Output

0

Note

The factorial of n is equal to the product of all integers from 1 to n inclusive, that is n! = 1·2·3·...·n.

In the first sample, 5! = 120, 6! = 720, 7! = 5040, 8! = 40320 and 9! = 362880.

题意:

给你一个m,让你给出n个数,这n个数中每个数的阶乘有m个0.

暴力规律:

思路:

阶乘说白了还是乘法,乘法可以分解成n个没有约数的数字,在这些数字的乘法中什么时候会产生0呢?毫无疑问只有当2*5的时候。所以一个阶乘中有几个5就会有几个0,因为只要是2的倍数就会提供2,一个数的阶乘中含的5的个数要比含有2的个数要少吧,所以2的数目是充足的,所以看阶乘最后有几个0只需要考虑有几个5就够了,含有5的个数可以直接暴力,因为数据不大,最多才含有100000个5。

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<sstream>
#include<vector>
#include<map>
#include<set>
#define up(i, x, y) for(int i = x; i <= y; i++)
#define down(i, x, y) for(int i = x; i >= y; i--)
using namespace std;
typedef long long ll;
int n, m, k;
int a, b;
ll T, t;
int ans = 0;
string str;
int main()
{
    scanf("%d", &m);
    n = m;
    int num = 0;
    for(int i = 5; i <= 500000; i += 5){//因为只有5的倍数可以分解出来5,所以直接+5就好
        t = i;
        while(t){
            if(t % 5 == 0){
                t /= 5;
                num++;  //从开始就统计5的个数
            }
            else break;
        }
        if(num >= m)//如果5的个数(相当于末尾0的个数)>= 预期的就进入
        {
            if(num == m){
                puts("5");
                up(j, 1, 5) printf("%d%c", i++, j == 5 ? '\n' : ' ');
                break;
            }
            else {  //比如5个0结尾的就不存在因为 25 ! 里面6个5    20!里面4个5
//这种现象是由于 25 == 5 * 5 ,一个数分解多个5 造成的
                puts("0");
                break;
            }
        }
    }
}

数学规律:

数学规律这里作为重点来讲解,思路非常简单,代码非常简洁,速度也快,下面详细说明:

先给出数学公式:
 

f(x)代表正整数x末尾有f(x)个0
f(x) = |  f(n!) = 0          (0 < n < 5)
       |  f(n!) = k + f(k!)  (n >= 5) 其中k = n / 5 向下取整

通俗的证明:

首先通过对刚才暴力规律的理解,相信大家理解了,计算n!的末尾0的个数,就相当于统计n!分解后出现了多少个5,那么n!什么情况下会分解出来5呢,毫无疑问只有n!里是5的倍数的数。例如: 31 ! = 31 * 30 * 29 * 28 * 27 * 26 ...... * 3 * 2 * 1  
那么31!末尾有多少个0,就看31!可以分解出来多少个5,看可以分解出来多少个5,就把能分解出5的数提取出来,则就是:
提取出里面5的倍数的数: 30  * 25 * 20 * 15 * 10 * 5 ( 31!里仅有这些数才可以分解出来5)
且    30  * 25 * 20 * 15 * 10 * 5    ==   5*6 * 5*5 * 5*4 * 5*3 * 5*2 * 5*1   ==   5^6  *  6!
所以31!末尾0的个数为  6 + 1(6!里含有5的个数)  ==   7个0。
大家观察一下这个 5 ^ 6  * 6 里的6怎么计算出来的呢? 不就是 : 31 / 5 向下取整嘛,至于为什么,不言而喻了~
如果推广到n呢? 
n! 末尾含有0的个数(或者说含有5的个数)为: 5^(n/5) * (n/5)!  == 5^k * k!  == (k个) +  (k! 里面含有5的个数)
所以总结出: 

f(x)代表正整数x末尾有f(x)个0
f(x) = |  f(n!) = 0          (0 < n < 5)
       |  f(n!) = k + f(k!)  (n >= 5) 其中k = n / 5 向下取整

所以直接一个递归就出来某个阶乘末尾有多少个0,有不明白的地方欢迎评论区提问~

其实这个题目是逆过来了,给你末尾0的个数让你求某个数的阶乘,这样可以利用二分来查找,因为结果不可能超过500000,所以速度还是比较快的。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<sstream>
#include<vector>
#include<map>
#include<set>
#define up(i, x, y) for(int i = x; i <= y; i++)
#define down(i, x, y) for(int i = x; i >= y; i--)
using namespace std;
typedef long long ll;
int n, m, k;
int a, b;
ll T, t;
int ans = 0;
string str;
int f(int n)
{
    if(n/5 == 0) return 0;
    return n / 5 + f(n / 5);
}
int main()
{
    scanf("%d", &n);
    int l = 0, r = 500000, mid, flag = 0;
    while(l <= r)
    {
        mid = (l + r) / 2;
        t = f(mid);
        if(t == n){
            puts("5");
            while(mid % 5 != 0) mid--;
            up(j, 1, 5) printf("%d%c", mid++, j == 5 ? '\n' : ' ');
            flag = 1;
            break;
        }
        if(t > n) r = mid - 1;
        if(t < n) l = mid + 1;
    }
    if(flag == 0) puts("0");
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值