题目描述
所有有经验的参赛者都知道,在阶乘 n!n!n! 中质数 ppp 的出现次数可以用以下公式计算:
f(n,p)=⌊np⌋+⌊np2⌋+⌊np3⌋+⌊np4⌋+⋯f(n,p) = \left\lfloor \frac{n}{p} \right\rfloor + \left\lfloor \frac{n}{p^2} \right\rfloor + \left\lfloor \frac{n}{p^3} \right\rfloor + \left\lfloor \frac{n}{p^4} \right\rfloor + \cdotsf(n,p)=⌊pn⌋+⌊p2n⌋+⌊p3n⌋+⌊p4n⌋+⋯
这个公式可以有效地用于计算任意进制下 n!n!n! 的尾随零的个数。设 z(n,b)z(n,b)z(n,b) 是一个函数,表示在进制 bbb 下 n!n!n! 的尾随零的个数。定义一个新函数 soz(n,b)soz(n,b)soz(n,b) 如下:
soz(n,b)=∑i=1nz(i,b)soz(n,b) = \sum_{i=1}^{n} z(i,b)soz(n,b)=i=1∑nz(i,b)
给定 nnn 和基数 bbb,你的任务是求出 soz(n,b)soz(n,b)soz(n,b) 的值。
输入限制:
- 0≤n≤40000000000 \leq n \leq 40000000000≤n≤4000000000
- 1<b≤1000001 < b \leq 1000001<b≤100000
- bbb 是无平方因子数(不能被任何平方数整除,除了 111)
- 最多 120012001200 行输入
题目分析
关键观察
-
无平方因子数的性质:由于 bbb 是无平方因子数,它的质因数分解形式为 b=p1×p2×⋯×pkb = p_1 \times p_2 \times \cdots \times p_kb=p1×p2×⋯×pk,其中每个质因子的指数都是 111。
-
尾随零的计算:在 bbb 进制下,n!n!n! 的尾随零个数由质因数分解中各个质因子在 n!n!n! 中出现次数的最小值决定:
z(n,b)=min{f(n,p1),f(n,p2),…,f(n,pk)}z(n,b) = \min\{f(n,p_1), f(n,p_2), \ldots, f(n,p_k)\}z(n,b)=min{f(n,p1),f(n,p2),…,f(n,pk)} -
关键优化:对于无平方因子数,尾随零个数实际上由 bbb 的最大质因子决定。这是因为较大的质因子在阶乘中出现的次数总是小于或等于较小质因子出现的次数:
z(n,b)=f(n,pmax)其中 pmax 是 b 的最大质因子z(n,b) = f(n, p_{\max}) \quad \text{其中 } p_{\max} \text{ 是 } b \text{ 的最大质因子}z(n,b)=f(n,pmax)其中 pmax 是 b 的最大质因子
数学推导
我们需要计算:
soz(n,b)=∑i=1nz(i,b)=∑i=1nf(i,pmax)soz(n,b) = \sum_{i=1}^n z(i,b) = \sum_{i=1}^n f(i, p_{\max})soz(n,b)=i=1∑nz(i,b)=i=1∑nf(i,pmax)
展开 f(i,pmax)f(i, p_{\max})f(i,pmax):
soz(n,b)=∑i=1n(⌊ipmax⌋+⌊ipmax2⌋+⌊ipmax3⌋+⋯ )soz(n,b) = \sum_{i=1}^n \left( \left\lfloor \frac{i}{p_{\max}} \right\rfloor + \left\lfloor \frac{i}{p_{\max}^2} \right\rfloor + \left\lfloor \frac{i}{p_{\max}^3} \right\rfloor + \cdots \right)soz(n,b)=i=1∑n(⌊pmaxi⌋+⌊pmax2i⌋+⌊pmax3i⌋+⋯)
交换求和顺序:
soz(n,b)=∑k=1∞∑i=1n⌊ipmaxk⌋soz(n,b) = \sum_{k=1}^{\infty} \sum_{i=1}^n \left\lfloor \frac{i}{p_{\max}^k} \right\rfloorsoz(n,b)=k=1∑∞i=1∑n⌊pmaxki⌋
令 S(n,m)=∑i=1n⌊i/m⌋S(n, m) = \sum_{i=1}^n \lfloor i/m \rfloorS(n,m)=∑i=1n⌊i/m⌋,则:
soz(n,b)=∑k=1∞S(n,pmaxk)soz(n,b) = \sum_{k=1}^{\infty} S(n, p_{\max}^k)soz(n,b)=k=1∑∞S(n,pmaxk)
高效计算 S(n,m)S(n, m)S(n,m)
对于 S(n,m)=∑i=1n⌊i/m⌋S(n, m) = \sum_{i=1}^n \lfloor i/m \rfloorS(n,m)=∑i=1n⌊i/m⌋,我们可以用数学公式直接计算:
设 q=⌊n/m⌋q = \lfloor n/m \rfloorq=⌊n/m⌋(完整周期的个数),r=n%mr = n \% mr=n%m(最后一个不完整周期的元素个数)。
- 前 q−1q-1q−1 个完整周期:每个周期贡献 0+1+2+⋯+(m−1)=m(m−1)20 + 1 + 2 + \cdots + (m-1) = \frac{m(m-1)}{2}0+1+2+⋯+(m−1)=2m(m−1)
- 最后一个不完整周期:贡献 q×(r+1)q \times (r + 1)q×(r+1)
因此:
S(n,m)=m×(q−1)q2+q×(r+1)S(n, m) = m \times \frac{(q-1)q}{2} + q \times (r + 1)S(n,m)=m×2(q−1)q+q×(r+1)
算法步骤
- 寻找最大质因子:对 bbb 进行质因数分解,找到最大的质因子 pmaxp_{\max}pmax
- 计算总和:对于 k=1,2,3,…k = 1, 2, 3, \ldotsk=1,2,3,…,计算 S(n,pmaxk)S(n, p_{\max}^k)S(n,pmaxk) 并累加,直到 pmaxk>np_{\max}^k > npmaxk>n
- 输出结果:对于每个测试用例输出计算得到的 soz(n,b)soz(n,b)soz(n,b)
复杂度分析
- 寻找最大质因子:O(b)O(\sqrt{b})O(b),由于 b≤100000b \leq 100000b≤100000,这是可接受的
- 计算总和:O(logpmaxn)O(\log_{p_{\max}} n)O(logpmaxn),由于 n≤4×109n \leq 4 \times 10^9n≤4×109,这非常高效
- 总体复杂度:对于 120012001200 个测试用例,总复杂度是可接受的
代码实现
// Zeroes Revisited
// UVa ID: 11083
// Verdict: Accepted
// Submission Date: 2025-11-19
// UVa Run Time: 0.000s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
// 获取b的最大质因子
ll getMaxPrimeFactor(ll b) {
ll maxPrime = 1;
for (ll p = 2; p * p <= b; ++p) {
if (b % p == 0) {
maxPrime = max(maxPrime, p);
while (b % p == 0) b /= p;
}
}
return max(maxPrime, b);
}
// 计算 ∑_{i=1}^n ⌊i/m⌋
ll sumFloor(ll n, ll m) {
ll quotient = n / m;
ll remainder = n % m;
return m * (quotient - 1) * quotient / 2 + quotient * (remainder + 1);
}
// 计算 soz(n, b)
ll soz(ll n, ll b) {
if (n == 0) return 0;
ll maxPrime = getMaxPrimeFactor(b);
ll sum = 0;
ll power = maxPrime;
while (power <= n) {
sum += sumFloor(n, power);
if (power > n / maxPrime) break;
power *= maxPrime;
}
return sum;
}
int main() {
ll n, b;
while (cin >> n >> b && (n || b)) {
cout << soz(n, b) << endl;
}
return 0;
}
总结
本题的关键在于利用无平方因子数的性质和数学推导,将问题转化为计算 ∑i=1nf(i,pmax)\sum_{i=1}^n f(i, p_{\max})∑i=1nf(i,pmax),然后通过数学公式直接计算,避免了低效的遍历和二分查找。这种数学优化的方法在处理大范围数据时非常有效。
9609

被折叠的 条评论
为什么被折叠?



