云计算百科
云计算领域专业知识百科平台

UVa 11802 All Your Bases Belong to Us

题目描述

给定两个非负整数 nnnkkk,求有多少个不同的进制 bbbb≥2b \\ge 2b2),使得 n!n!n!bbb 进制下末尾恰好有 kkk 个零。

输入格式

输入第一行包含一个正整数 TTTT≤10000T \\le 10000T10000),表示测试用例数。
接下来 TTT 行,每行两个整数 nnnkkk,满足 n≤1015n \\le 10^{15}n10151≤k≤10151 \\le k \\le 10^{15}1k1015,并且保证 n/k<500n / k < 500n/k<500

输出格式

对于每个测试用例,输出一行,格式为 Case X: Y,其中 XXX 是测试用例编号(从 111 开始),YYY 是答案对 100000000710000000071000000007 取模后的值。

样例

输入

5
10 2
10 3
10 4
10 5
10 8

输出

Case 1: 24
Case 2: 0
Case 3: 4
Case 4: 0
Case 5: 1

题目分析

本题是经典问题“阶乘末尾零”的逆向版本。已知 n!n!n! 在十进制下的末尾零个数由因子 10=2×510 = 2 \\times 510=2×5 决定,更一般地,在进制 bbb 中,若 b=∏pieib = \\prod p_i^{e_i}b=piei,则 n!n!n!bbb 进制下末尾零的个数为

min⁡i⌊vpi(n!)ei⌋,
\\min_i \\left\\lfloor \\frac{v_{p_i}(n!)}{e_i} \\right\\rfloor,
imineivpi(n!),

其中 vp(n!)v_p(n!)vp(n!) 表示 n!n!n! 中质因子 ppp 的指数。题目要求这个最小值恰好等于 kkk

直接枚举 bbb 的范围显然不可行(bbb 可以大到 n!n!n! 本身)。需要利用 n/k<500n/k < 500n/k<500 这一关键条件。因为若某个质数 p>n/k+1p > n/k + 1p>n/k+1,则 vp(n!)≤n/(p−1)<kv_p(n!) \\le n/(p-1) < kvp(n!)n/(p1)<k,这样的质数不可能在 bbb 中出现(否则指数 ei≥1e_i \\ge 1ei1 会导致最小值小于 kkk)。因此,只需考虑所有不超过 500500500 的质数,问题规模大幅缩小。

解题思路

定义与符号

vp=vp(n!)v_p = v_p(n!)vp=vp(n!)。对于任意正整数 KKK,定义 f(K)f(K)f(K) 为满足“末尾零个数至少为 KKK”的进制数(包括 b=1b=1b=1 这个平凡情况)的个数。根据上述最小值的条件,bbb 的质因数分解中,每个质因子 ppp 的指数 epe_pep 必须满足

⌊vpep⌋≥K⟺ep≤⌊vpK⌋.
\\left\\lfloor \\frac{v_p}{e_p} \\right\\rfloor \\ge K \\quad \\Longleftrightarrow \\quad e_p \\le \\left\\lfloor \\frac{v_p}{K} \\right\\rfloor.
epvpKepKvp.

因此,每个 ppp 可选的指数范围为 0,1,…,⌊vp/K⌋0, 1, \\dots, \\left\\lfloor v_p / K \\right\\rfloor0,1,,vp/K,其中 000 表示该质因子不出现。独立选择各质因子的指数即可确定一个 bbb(注意 b=1b=1b=1 对应所有指数均为 000),所以

f(K)=∏p: vp≥K(⌊vpK⌋+1).
f(K) = \\prod_{p:\\, v_p \\ge K} \\left( \\left\\lfloor \\frac{v_p}{K} \\right\\rfloor + 1 \\right).
f(K)=p:vpK(Kvp+1).

这里乘积中仅包含满足 vp≥Kv_p \\ge KvpK 的质数,因为若 vp<Kv_p < Kvp<K,则 ⌊vp/K⌋=0\\lfloor v_p / K \\rfloor = 0vp/K=0,只有指数 000 可选,因子为 111,不贡献。

容斥求恰好 kkk 个零

g(K)g(K)g(K) 表示满足“末尾零个数恰好为 KKK”的进制数(b≥2b \\ge 2b2)。显然,恰好为 kkk 的个数等于至少为 kkk 的个数减去至少为 k+1k+1k+1 的个数:

g(k)=(f(k)−1)−(f(k+1)−1)=f(k)−f(k+1).
g(k) = \\left( f(k) – 1 \\right) – \\left( f(k+1) – 1 \\right) = f(k) – f(k+1).
g(k)=(f(k)1)(f(k+1)1)=f(k)f(k+1).

因为 f(K)f(K)f(K) 包含了 b=1b=1b=1,而题目要求 b≥2b \\ge 2b2,减去 111 后相减抵消,故答案即 f(k)−f(k+1)f(k) – f(k+1)f(k)f(k+1)

计算 vp(n!)v_p(n!)vp(n!)

利用勒让德公式:

vp(n!)=∑j≥1⌊npj⌋.
v_p(n!) = \\sum_{j \\ge 1} \\left\\lfloor \\frac{n}{p^j} \\right\\rfloor.
vp(n!)=j1pjn.

由于 p≤500p \\le 500p500n≤1015n \\le 10^{15}n1015,直接用循环累除计算即可,复杂度极小。

算法步骤

  • 预处理出所有不超过 500500500 的质数。
  • 对于每个测试用例 (n,k)(n, k)(n,k)
    • 初始化两个乘积 prod1=1\\textit{prod1} = 1prod1=1(对应 K=kK = kK=k)和 prod2=1\\textit{prod2} = 1prod2=1(对应 K=k+1K = k+1K=k+1)。
    • 遍历每个质数 ppp(若 p>np > np>n 则跳过,因为 vp=0v_p = 0vp=0):
      • 计算 v=vp(n!)v = v_p(n!)v=vp(n!)
      • 如果 v≥kv \\ge kvk,则 prod1←prod1×(⌊v/k⌋+1) mod MOD\\textit{prod1} \\gets \\textit{prod1} \\times (\\lfloor v/k \\rfloor + 1) \\bmod \\textit{MOD}prod1prod1×(⌊v/k+1)modMOD
      • 如果 v≥k+1v \\ge k+1vk+1,则 prod2←prod2×(⌊v/(k+1)⌋+1) mod MOD\\textit{prod2} \\gets \\textit{prod2} \\times (\\lfloor v/(k+1) \\rfloor + 1) \\bmod \\textit{MOD}prod2prod2×(⌊v/(k+1)⌋+1)modMOD
    • 答案 =(prod1−prod2+MOD) mod MOD= (\\textit{prod1} – \\textit{prod2} + \\textit{MOD}) \\bmod \\textit{MOD}=(prod1prod2+MOD)modMOD,输出。
  • 正确性证明

    由上述分析,f(K)f(K)f(K) 的计算正确反映了所有满足至少 KKK 个零的进制数(包括 b=1b=1b=1)。由于 b=1b=1b=1 在所有 KKK 下都被计入,容斥后恰好得到 b≥2b \\ge 2b2 的个数。同时,由于 n/k<500n/k < 500n/k<500,所有可能影响结果的质数都被覆盖,遗漏的质数不会改变乘积(其对应因子为 111)。

    复杂度分析

    • 预处理质数:O(500log⁡log⁡500)O(500 \\log \\log 500)O(500loglog500)
    • 每个测试用例:遍历不超过 500500500 的质数个数(约 959595 个),每次计算 vp(n!)v_p(n!)vp(n!) 的循环次数为 log⁡pn≤log⁡21015≈50\\log_p n \\le \\log_2 10^{15} \\approx 50logpnlog2101550。总操作量约为 T×95×50T \\times 95 \\times 50T×95×50,当 T=10000T = 10000T=10000 时约为 4.75×1074.75 \\times 10^74.75×107,完全可以接受。
    • 时间复杂度:O(T⋅π(500)⋅log⁡n)O(T \\cdot \\pi(500) \\cdot \\log n)O(Tπ(500)logn),空间复杂度 O(1)O(1)O(1)

    代码实现

    // All Your Bases Belong to Us
    // UVa ID: 11802
    // Verdict: Accepted
    // Submission Date: 2026-06-20
    // UVa Run Time: 0.050s
    //
    // 版权所有(C)2026,邱秋。metaphysis # yeah dot net

    #include <bits/stdc++.h>
    using namespace std;

    typedef long long int64;
    const int MOD = 1000000007;
    const int MAXP = 500;

    vector<int> getPrimes(int limit) {
    vector<bool> isComp(limit + 1, false);
    vector<int> primes;
    for (int i = 2; i <= limit; ++i) {
    if (!isComp[i]) {
    primes.push_back(i);
    if ((int64)i * i <= limit)
    for (int j = i * i; j <= limit; j += i) isComp[j] = true;
    }
    }
    return primes;
    }

    int main() {
    vector<int> primes = getPrimes(MAXP);
    int T;
    scanf("%d", &T);
    for (int caseNo = 1; caseNo <= T; ++caseNo) {
    int64 n, k;
    scanf("%lld %lld", &n, &k);
    int64 prod1 = 1; // 对应 K = k
    int64 prod2 = 1; // 对应 K = k+1
    for (int p : primes) {
    if (p > n) break;
    int64 m = 0;
    int64 tmp = n;
    while (tmp) {
    tmp /= p;
    m += tmp;
    }
    if (m >= k) {
    int64 cnt = m / k + 1;
    prod1 = prod1 * (cnt % MOD) % MOD;
    }
    if (m >= k + 1) {
    int64 cnt = m / (k + 1) + 1;
    prod2 = prod2 * (cnt % MOD) % MOD;
    }
    }
    int64 ans = (prod1 prod2 + MOD) % MOD;
    printf("Case %d: %lld\\n", caseNo, ans);
    }
    return 0;
    }

    总结

    本题的关键在于利用 n/k<500n/k < 500n/k<500 的条件将需要考虑的质数范围缩小到 500500500 以内,从而避免了枚举所有可能的进制 bbb。通过容斥原理,将“恰好为 kkk”转化为两个“至少为 KKK”的计数相减,而后者可以通过质因数分解和乘法原理轻松求出。

    该解法体现了以下技巧:

    • 缩小规模:利用数据范围限制过滤无关质数。
    • 容斥转换:将精确条件转化为两个下界条件的差。
    • 模运算:注意取模,避免溢出。

    此方法对同类问题(如求阶乘在给定进制下末尾零个数的逆向问题)具有通用性,值得借鉴。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » UVa 11802 All Your Bases Belong to Us
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!