题目描述
给定一个非负整数 NN,返回 N!N! 结果的末尾为 00 的数量。
N!N! 是指自然数 NN 的阶乘,即:N!=123…(N-2)*(N-1)*NN!=1∗2∗3…(N−2)∗(N−1)∗N。
示例2
输入 5
返回值 1 (说明 5!=120 )
求解矩阵末尾0的个数可以看成是求解在阶乘最终结果中可以整除10次数,也可以看成是求解在1~n中有多少个2的倍数记为 p 和 多少个5的倍数记为 q,在 1 ~n中,p > q ,原因如下。
计算p和q的统一公式如下:n/p + n/p2+n/p3+…… 这些指的是整除,下取整。
该公式的含义可以理解为下取整意味着存在一个数 k ,使得 k可以整除p,举个例子,比如 n == 5,5/2 意味着现在我使用到的数字是4和2, 5/4 由于4还包含另一个2,因此现在使用到的数字是4。通俗理解可以结合筛质数的方法(埃式筛质法原理:如果一个数是质数,那么该质数的平方就不是质数,三次方也不是质数,这类数字都必须要筛掉),只不过在本例中计算倍数时需要将这些数字求和。
理解了公式以后对于本题目就只需要求解1~n 中有多少个5的倍数。计算公式就是那行代码,每次循环分子都会在原有基础上除以5,也就是除以平方,三次方这样的含义。
class Solution {
public:
/**
* the number of 0
* @param n long长整型 the number
* @return long长整型
*/
long long thenumberof0(long long n) {
// write code here
long long res=0;
while(n!=0)
{
res+=n/5,n/=5;
}
return res;
}
};
扩展题目
题目一
给定整数 N,试把阶乘 N! 分解质因数,按照算术基本定理的形式输出分解结果中的 pi 和 ci 即可。
输入格式,一个整数 N。
输出格式,N! 分解质因数后的结果,共若干行,每行一对 pi,ci,表示含有 pcii 项。按照 pi 从小到大的顺序输出。
数据范围:1≤N≤106
示例:
输入样例:
5
输出样例:
2 3
3 1
5 1
样例解释
5!=120=23∗3∗5
该题目可以看作上一个题目的扩展,上一个题目只是求解 1~n 中有多少个5的倍数,这次求解的是在中 1~n 中有多少个数是N!所包含的质因数的倍数,比如样例5!,该数字的质因数是 2 ,3 ,5。分别求解这几个数字在1~n 中有多少个数是这些质因数的倍数。
其次还有个问题就是要求解 5! 所包含的质因数,可以转化为求解 5 以内每个数包含的质数分别是哪些,原因在于 5!=54321 ,乘积之后数字变大,要求解质因数不需要将完整的阶乘算出来再求解。
n = 1e7的时候线性筛质法基本就比埃式筛法快一倍了。
算法核心:n仅会被其最小质因子pj筛掉。
- 当n为一个合数的时候,如果i比n / pj还小,就一定会被筛掉
- 判断i%pj ==0,pj一定是i的最小质因子,pj也一定是pj*i最小质因子
- 判断i%pj != 0,pj一定小于i的所有质因子,pj也一定是pj*i最小质因子
- 对于一个合数x,假设pj是x的最小质因子,当i枚举到x /pj的时候,一定会被筛掉。
- for(int j = 0; primes[j] <= n / i; j ++)中注意下是<=。
#include<iostream>
#include<cstring>
using namespace std;
const int N=1000010;
bool st[N];
int cnt=0;
int primes[N];
//线性筛质法
void get_primes(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i]) primes[cnt++]=i;
for(int j=0;primes[j]<=n/i;j++)
{
st[primes[j]*i]=true;
if(i%primes[j]==0) break;
}
}
}
int main()
{
int n;
cin>>n;
get_primes(n);
for(int i=0;i<cnt;i++)
{
int p = primes[i];
int s=0,t=n;
while(t) s+=t/p,t/=p;
printf("%d %d\n",p,s);
}
return 0;
}
题目二