CF1285F Classical

Description

给定 nn 个数 a1,,ana_1,\cdots, a_n ,求两两 lcm\text{lcm} 的最大值。
1n,ai1051\leq n,a_i\leq 10^5

Solution

nb 题。
考虑枚举 gcd=d\gcd=d 。将 dd 的倍数放在同一个集合内,问题转化为在集合内找一组 (a,b)(a,b) ,满足 gcd(a,b)=d\gcd(a,b)=d ,且 a×ba\times b 最大。

先将集合内的元素都除以 dd ,则要求变成 gcd(a,b)=1\gcd(a,b)=1
我们将元素从大至小排序,并逐个枚举。设当前枚举的是 xx 。假如曾经枚举过一个数 zz ,满足 gcd(x,z)=1\gcd(x,z)=1事实上 zxz\sim x 之间的元素就没有用处了。所以我们可以拿栈来维护,每次判定是否存在 zz 使得 gcd(x,z)=1\gcd(x,z)=1 ,然后不断弹栈。

问题转化为如何快速判定一个集合内有多少个数与当前数互质。设 cic_i 表示 ii 的出现次数,也就是求:

i0ci[gcd(i,x)=1]\sum_{i\ge 0}c_i[\gcd(i,x)=1]

莫比乌斯反演一下可以得到

txμtitci\sum_{t|x}\mu_t\sum_{i|t}c_i

所以可以每次加入集合/弹出集合时暴力给因数的 cic_i 加/减一,查询时枚举因数求和。

复杂度 O(nlog22n)\mathcal{O}(n\log_2^2n)。每个数均摊入 log2\log_2 次栈,栈内元素求 gcd\gcd 需要一个 log2n\log_2n

Code

const int N = 1e5 + 5;
using namespace std;
int n, m, num;
int a[N], b[N], p[N], s[N], mu[N], cnt[N];
vector<int>d[N];
bitset<N>ok;
void Prework(int n){
    mu[1] = 1;
    for(int i = 2; i <= n; ++i){
        if(!ok[i]) p[++num] = i, mu[i] = -1;
        for(int j = 1; j <= num and p[j] * i <= n; ++j){
            ok[i * p[j]] = 1;
            if(i % p[j] == 0) break;
            mu[i * p[j]] = -mu[i];
        }
    }
    for(int i = 1; i <= n; ++i)
    for(int j = i; j <= n; j += i)
        d[j].push_back(i);
}
int gcd(int x, int y){
    return (!y) ? x : gcd(y, x % y); 
}
int main(){
    
    Prework(1e5);

    r(n);
    for(int i = 1; i <= n; ++i)
        r(a[i]), ++b[a[i]], m = max(a[i], m);
    long long ans = 0;
    vector<int> v;
    for(int i = 1; i <= m; ++i){
        v.clear();
        for(int j = i; j <= m; j += i)
        for(int k = 1; k <= b[j]; ++k)
            v.push_back(j / i);
        int top = 0;
        reverse(v.begin(), v.end());
        for(auto x:v){
            int res = 0;
            for(auto y:d[x]) res += cnt[y] * mu[y];
            while(res){
                int z = s[top];
                if(gcd(z, x) == 1)
                    ans = max(1ll * x * z * i, ans), --res;
                for(auto y:d[z]) --cnt[y];
                --top;
            }
            s[++top] = x;
            for(auto y:d[x]) ++cnt[y];
        }
        while(top) {
            int x = s[top];
            for(auto y:d[x]) --cnt[y]; 
            --top;
        }
    }
    w(ans);
    return 0;
}