LibreOJ - 530 最小倍数 数论,按位贪心
题意
给定(p),求最小的正整数(n),使得$n! $ $mod $ $ p =0$
由于(p)很大,输入将给出质因子的分解形式,输入将给出(m)和(e_1,e_2,...e_m),表示(p = prod pr^{e_i}_{i})
(pr_i)表示从小到大的第(i)个质数
输入一共有(T)组
分析
最暴力的方法:
枚举答案(n),对于每个(pr_i)检查是否满足(n!) (mod) (pr_i^{e_i} = 0) .
具体实现时,可以找到最大的(alpha_i)使得(pr_i^{alpha}|n!),检查是否有(alpha_i > e_i)即可
计算(alpha_i)有一个很简单的方法:从(1 imes 2 imes3... imes N)中提取出(pri)的倍数,并从每个倍数中提取出一个(pri),剩下可能有(pri)的倍数的部分是
于是(alpha = sum_{k=1}^infty lfloor frac{N}{pr^k} floor)
注意到这样写过于暴力我们尝试优化
很容易估算出答案的一个上界,不妨二分答案
二分答案后每一个(alpha_i)的复杂度是(O(log_{pr}N)),总的复杂度(O(Tmlog^2a_i))
再进行优化
注意到(alpha_i = sum_{k=1}^infty lfloor frac{N}{pr^k} floor)这个式子可以并行运算,换句话说,将(N)转化成(pr_i)进制,不同位上对(alpha_i)贡献互不影响,可以分别计算并累加
(N)在(pr_i)进制下的某数位(v imes pr_i ^ k)的贡献就是((vvv...v)_{pr_i})共(k)个(v)。由此可以确定第(k)个数位对(alpha)的贡献,因此只需要从高位开始贪心的找到一个(N)使得(alpha_i >= e)取这些(N)与(1)的最大值即可
时间复杂度(O(Tmloga_i))
代码
int vis[1005];
int prime[1005];
void euler_sieve(int n) {
int cnt = 0;
for (int i = 2; i < n; i++) {
if (!vis[i]) prime[cnt++] = i;
for (int j = 0; j < cnt; j++) {
if (i * prime[j] > n) break;
vis[i * prime[j]] = 1;
if (i % prime[j] == 0) break;
}
}
}
int main() {
euler_sieve(1005);
int T = readint();
while (T--) {
int m = readint();
ll ans = 1;
for (int i = 0; i < m; i++) {
ll e = readll();
ll cnt = 0;
ll base = 1, val = prime[i], p = prime[i];
while (base * p + 1 <= e)
base = base * p + 1, val *= p;
for (; base; base /= p, val /= p)
cnt += val * (e / base), e -= base * (e / base);
ans = max(ans, cnt);
}
Put(ans);
puts("");
}
}