CF 1285 F. Classical?

题目大意

给定数组\(a\) ,选两个数,使得最小公倍数最大,求最小公倍数的值。

限制:长度\(n(2\leq n \leq 10^5)\), \(1 \le a_i \le 10^5\)

讲解

看似很经典的题目,但需要一些前置知识。

前置知识1 \(O(n \ log \ n)\) 求Mobius函数

虽然存在近似线性的求法,但这个方法代码很短。
代码如下:

    mu[1]=1;
    for(int i=1;i<N;i++){
        for(int j=2*i;j<N;j+=i){
            mu[j]-=mu[i];
        }
    }

为什么这个方法是对的?
这是由如下定理保证的:

定理 1
\[ \Sigma_{d | n} \mu (d)=\begin{cases} 0 & n \neq 1 \\ 1 & n = 1 \end{cases}\]

证明:
n = 1时显然。
当n>1时,对n进行唯一分解,设有k个素数:
\[n=\Pi ^{k}_{i=1} {p_i}^{m_i}\]
现在考虑n的全部因子,如果那个因子的某个\(m_i \geq 2\),由Mobius函数的定义,它的值为0,所以值考虑\(m_i = 0,1\)的因子。

假设\(m_i\)中1的个数是\(u\)个,则所有\(m_i\)分布情况有\(C_k^u\)种,于是。
\[ \Sigma_{d | n} \mu (d)= \Sigma _{u=1}^{k} (-1)^k C_k^u\]
有二项式定理得:
\[ \Sigma _{u=1}^{k} (-1)^k C_k^u = 0\]
故:
\[ \Sigma_{d | n} \mu (d)= 0 \]

前置知识2

定理2 给定数列\(a\)和一个数\(x\),数列\(a\)中与\(x\)互素的个数为:
\[\sum_{d|x} \mu(d) cnt_d\]
其中,\(cnt_d\)的意思是,数列\(a\)中有多少个数是\(d\)的倍数。

限于我的水平,无法证明此定理。但我知道大概的意思是,假如\(d=1\)那么总数是\(x\)个,假设\(x\)拥有素因子2,3,5,11,那么需要减掉2,3,5,11的倍数的数的个数,但这样既是2的倍数又是3的倍数的数(6)又被重复减掉了,所以要加回来,以此类推系数正是Mobius函数。

前置知识3

一个数\(n\)的因子的个数是\(O(n \ log \ n)\)级别的。

扫描二维码关注公众号,回复: 8826354 查看本文章

步骤1

由于\(LCM(x,y) = \frac{xy}{g}\),其中\(g\)表示\(x\)\(y\)的最大公因数。接下来的做法是,我们要枚举g,然后企图快速更新答案。

枚举\(g\),对所有\(g\)的倍数\(b\),计算\(b/g\)放到一个数组里面。在这个数组里面,如果两个数\(u\)\(v\)互素,那么就可以用\(uvg\)更新答案了。

这是我们目前的一个想法。到这里,假设能瞬间(不妨设为\(O(c)\)的时间)找到数组里面最大的两个互素的数,是否可行呢?

当然可以。因为,\(a_i\)的值域是\(10^5\)\(g\)最大枚举\(10^5\),每个数最多被插入一个数组的次数是它因子的个数\(d\),所以到这一步,花费的时间是\(O(10^5 * d)\)

步骤2

现在的子问题是:给你一个数组,快速找到两个互素的数,且他们的乘积是最大的。

首先,我们需要一个空的栈。

现在,对于这个数组,我们先按从大到小排序。现在左边大,右边小。

现在以此从左到右处理,拿到一个数\(x\),准备丢到栈中。但是,在丢之前先看看,栈里有没有和x互素的数,这用前置知识2计算,由前置知识3,这需要\(O(log \ n)\)的时间。

如果栈里有\(\sigma\)个互素的数,那么,弹出\(\sigma\)个与\(x\)互素的数。
并遇到互素的数时,让答案尽量更新到最大,再让\(\sigma\)--。

这样是不会漏掉最优解的,因为后面要插入的数比现在的\(x\)还小,而弹出的数,比最后一个弹出的数小。

代码

https://codeforces.com/contest/1285/submission/69300157

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N = 1e5+1;

int n,m;
int mu[N];
int S[N],t;
vector<int> d[N];
int exist[N];
vector<int> v[N];   // v[g][i] := g*v[g][i] 的值是存在于a[i]的
int cnt[N];

int main() {
    ios::sync_with_stdio(0);
    cin>>n;
    mu[1]=1;
    for(int i=1;i<N;i++){
        for(int j=i;j<N;j+=i){
            if(i!=j) mu[j]-=mu[i];
            d[j].push_back(i);
        }
    }
    long long ans = 0;
    for(int x,i=1;i<=n;i++) {
        cin>>x;
        exist[x]++;
        ans = max(ans,1ll*x);
    }
    for(int g=1;g<N;g++) {
        for(int j=g;j<N;j+=g) {
            for(int k=0;k<exist[j];k++) v[g].push_back(j/g);
        }
        reverse(v[g].begin(),v[g].end());
        // if(v[g].size()) cout<<g<<" - ";
        for(auto x:v[g]) {
            // if(v[g].size()) cout<<x<<" "<<endl;
            int sigma = 0;
            for(auto u:d[x]) sigma += mu[u] * cnt[u];
            // cout<<sigma<<endl;
            while(sigma>0 && t) {
                int u = S[--t];
                if(__gcd(u,x)==1) {
                    ans = max(ans,1ll * g * u * x);
                    sigma--;
                }
                for(auto w:d[u]) {
                    cnt[w]--;   
                }
            }
            S[t++] = x;
            for(auto u:d[x]) cnt[u]++;
        }
        // if(v[g].size()) cout<<endl;
        while(t) {
            int u = S[--t];
            for(auto w:d[u]) {
                cnt[w]--;
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/scnucjh/p/12232545.html