题目描述
给定两个非负整数 nnn 和 kkk,求有多少个不同的进制 bbb(b≥2b \\ge 2b≥2),使得 n!n!n! 在 bbb 进制下末尾恰好有 kkk 个零。
输入格式
输入第一行包含一个正整数 TTT(T≤10000T \\le 10000T≤10000),表示测试用例数。
接下来 TTT 行,每行两个整数 nnn 和 kkk,满足 n≤1015n \\le 10^{15}n≤1015,1≤k≤10151 \\le k \\le 10^{15}1≤k≤1015,并且保证 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 进制下末尾零的个数为
mini⌊vpi(n!)ei⌋,
\\min_i \\left\\lfloor \\frac{v_{p_i}(n!)}{e_i} \\right\\rfloor,
imin⌊eivpi(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/(p−1)<k,这样的质数不可能在 bbb 中出现(否则指数 ei≥1e_i \\ge 1ei≥1 会导致最小值小于 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.
⌊epvp⌋≥K⟺ep≤⌊Kvp⌋.
因此,每个 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:vp≥K∏(⌊Kvp⌋+1).
这里乘积中仅包含满足 vp≥Kv_p \\ge Kvp≥K 的质数,因为若 vp<Kv_p < Kvp<K,则 ⌊vp/K⌋=0\\lfloor v_p / K \\rfloor = 0⌊vp/K⌋=0,只有指数 000 可选,因子为 111,不贡献。
容斥求恰好 kkk 个零
令 g(K)g(K)g(K) 表示满足“末尾零个数恰好为 KKK”的进制数(b≥2b \\ge 2b≥2)。显然,恰好为 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 2b≥2,减去 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!)=j≥1∑⌊pjn⌋.
由于 p≤500p \\le 500p≤500 且 n≤1015n \\le 10^{15}n≤1015,直接用循环累除计算即可,复杂度极小。
算法步骤
- 初始化两个乘积 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 kv≥k,则 prod1←prod1×(⌊v/k⌋+1) mod MOD\\textit{prod1} \\gets \\textit{prod1} \\times (\\lfloor v/k \\rfloor + 1) \\bmod \\textit{MOD}prod1←prod1×(⌊v/k⌋+1)modMOD。
- 如果 v≥k+1v \\ge k+1v≥k+1,则 prod2←prod2×(⌊v/(k+1)⌋+1) mod MOD\\textit{prod2} \\gets \\textit{prod2} \\times (\\lfloor v/(k+1) \\rfloor + 1) \\bmod \\textit{MOD}prod2←prod2×(⌊v/(k+1)⌋+1)modMOD。
- 答案 =(prod1−prod2+MOD) mod MOD= (\\textit{prod1} – \\textit{prod2} + \\textit{MOD}) \\bmod \\textit{MOD}=(prod1−prod2+MOD)modMOD,输出。
正确性证明
由上述分析,f(K)f(K)f(K) 的计算正确反映了所有满足至少 KKK 个零的进制数(包括 b=1b=1b=1)。由于 b=1b=1b=1 在所有 KKK 下都被计入,容斥后恰好得到 b≥2b \\ge 2b≥2 的个数。同时,由于 n/k<500n/k < 500n/k<500,所有可能影响结果的质数都被覆盖,遗漏的质数不会改变乘积(其对应因子为 111)。
复杂度分析
- 预处理质数:O(500loglog500)O(500 \\log \\log 500)O(500loglog500)。
- 每个测试用例:遍历不超过 500500500 的质数个数(约 959595 个),每次计算 vp(n!)v_p(n!)vp(n!) 的循环次数为 logpn≤log21015≈50\\log_p n \\le \\log_2 10^{15} \\approx 50logpn≤log21015≈50。总操作量约为 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)⋅logn)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”的计数相减,而后者可以通过质因数分解和乘法原理轻松求出。
该解法体现了以下技巧:
- 缩小规模:利用数据范围限制过滤无关质数。
- 容斥转换:将精确条件转化为两个下界条件的差。
- 模运算:注意取模,避免溢出。
此方法对同类问题(如求阶乘在给定进制下末尾零个数的逆向问题)具有通用性,值得借鉴。
网硕互联帮助中心




评论前必须登录!
注册