LibreOJ - 530 最小倍数 数论,按位贪心
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\times 2\times3...\times N\)中提取出\(pri\)的倍数,并从每个倍数中提取出一个\(pri\),剩下可能有\(pri\)的倍数的部分是
于是\(\alpha = \sum_{k=1}^\infty \lfloor \frac{N}{pr^k} \rfloor\)
注意到这样写过于暴力我们尝试优化
很容易估算出答案的一个上界,不妨二分答案
二分答案后每一个\(\alpha_i\)的复杂度是\(O(log_{pr}N)\),总的复杂度\(O(Tmlog^2a_i)\)
再进行优化
注意到\(\alpha_i = \sum_{k=1}^\infty \lfloor \frac{N}{pr^k} \rfloor\)这个式子可以并行运算,换句话说,将\(N\)转化成\(pr_i\)进制,不同位上对\(\alpha_i\)贡献互不影响,可以分别计算并累加
\(N\)在\(pr_i\)进制下的某数位\(v\times 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("");
}
}