题目链接

求表达式 f(n)结果末尾0的个数

题目描述

输入一个自然数 ,求表达式 的结果末尾有几个连续的

解题思路

一个数末尾连续 的个数,取决于其质因数分解中因子 的个数。由于 ,并且在阶乘的质因数分解中,因子 的数量总是远多于因子 的数量,因此问题等价于求解 的质因数分解中总共有多少个因子

是一个连乘积,根据对数性质(这里是指数性质), 中因子 的总数等于每一项 中因子 的数量之和。

表示 末尾 的个数,也即 中因子 的数量。则题目所求为:

直接计算每个 再求和效率较低。我们可以换一个角度来统计。

考虑 的另一种表示形式:

中因子 的总数,等于 ,其中 是数字 本身含有的因子 的数量。

只有当 的倍数时, 才不为

  • 对于 ,它们至少含有一个因子
  • 对于 ,它们至少含有两个因子 (其中一个已经在上一层被统计,所以这里要额外统计一次)。
  • 对于 ,它们至少含有三个因子 (需要再额外统计一次)。
  • 以此类推。

我们可以按 的幂次 () 分层来统计贡献: 总因子 的数量 = (所有 的倍数的贡献和) + (所有 的倍数的额外贡献和) + (所有 的倍数的额外贡献和) +

对于 的所有倍数 ,它们每一个都会为总和贡献

所以,对于给定的 ,它贡献的总和为:

这是一个等差数列求和。令 的倍数的个数。

最终的答案就是 ,直到

算法流程

  1. 初始化总数为
  2. 开始循环。
  3. 在循环中,计算当前 值下的贡献和 ,并累加到总数中。
  4. 更新
  5. 时,循环结束。

代码

#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()

算法及复杂度

  • 算法:数论、数学求和

  • 时间复杂度。循环的次数取决于 的幂次增长到 所需的步数。

  • 空间复杂度。只使用了有限个变量来存储中间结果。