LibreOJ 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)

[Tleq 10^4\ 0leq a_ileq10^{18}\ 1leq m leq 100 ]

分析

最暴力的方法:
枚举答案(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)的倍数的部分是

[1 imes2 imes3... imes lfloorfrac{N}{pri_i} floor ]

于是(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("");
    }
}