题目链接
题目描述
输入一个自然数 ,求表达式
的结果末尾有几个连续的
?
解题思路
一个数末尾连续 的个数,取决于其质因数分解中因子
的个数。由于
,并且在阶乘的质因数分解中,因子
的数量总是远多于因子
的数量,因此问题等价于求解
的质因数分解中总共有多少个因子
。
是一个连乘积,根据对数性质(这里是指数性质),
中因子
的总数等于每一项
中因子
的数量之和。
令 表示
末尾
的个数,也即
中因子
的数量。则题目所求为:
直接计算每个 再求和效率较低。我们可以换一个角度来统计。
考虑 的另一种表示形式:
中因子
的总数,等于
,其中
是数字
本身含有的因子
的数量。
只有当 是
的倍数时,
才不为
。
- 对于
,它们至少含有一个因子
。
- 对于
,它们至少含有两个因子
(其中一个已经在上一层被统计,所以这里要额外统计一次)。
- 对于
,它们至少含有三个因子
(需要再额外统计一次)。
- 以此类推。
我们可以按 的幂次 (
) 分层来统计贡献:
总因子
的数量 = (所有
的倍数的贡献和) + (所有
的倍数的额外贡献和) + (所有
的倍数的额外贡献和) +
对于 的所有倍数
,它们每一个都会为总和贡献
。
所以,对于给定的 ,它贡献的总和为:
这是一个等差数列求和。令 为
的倍数的个数。
最终的答案就是 ,直到
。
算法流程:
- 初始化总数为
。
- 从
开始循环。
- 在循环中,计算当前
值下的贡献和
,并累加到总数中。
- 更新
。
- 当
时,循环结束。
代码
#include <iostream>
using namespace std;
using ll = long long;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
ll n;
cin >> n;
ll total_zeros = 0;
ll p = 5;
while (p <= n) {
ll num_multiples = n / p;
ll sum_for_p = num_multiples * (n + 1) - p * num_multiples * (num_multiples + 1) / 2;
total_zeros += sum_for_p;
// 防止 p*5 溢出 long long
if (p > n / 5) {
break;
}
p *= 5;
}
cout << total_zeros << endl;
return 0;
}
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
long n = sc.nextLong();
long totalZeros = 0;
long p = 5;
while (p <= n) {
long numMultiples = n / p;
long sumForP = numMultiples * (n + 1) - p * numMultiples * (numMultiples + 1) / 2;
totalZeros += sumForP;
// 防止 p * 5 溢出 long
if (p > n / 5) {
break;
}
p *= 5;
}
System.out.println(totalZeros);
}
}
import sys
def solve():
try:
n_str = sys.stdin.readline()
if not n_str:
return
n = int(n_str)
total_zeros = 0
p = 5
while p <= n:
num_multiples = n // p
sum_for_p = num_multiples * (n + 1) - p * num_multiples * (num_multiples + 1) // 2
total_zeros += sum_for_p
p *= 5
print(total_zeros)
except (IOError, ValueError):
return
solve()
算法及复杂度
-
算法:数论、数学求和
-
时间复杂度:
或
。循环的次数取决于
的幂次增长到
所需的步数。
-
空间复杂度:
。只使用了有限个变量来存储中间结果。