求数列中任意两数lcm最大值
|Word count:752|Reading time:3min|Post View:
题解
给定序列$a$,求$max{ lcm(a_i,a_j)}(n,a_i\leq 10^5)$
首先考虑将序列排序去重,并在过程中计算任意相等两数$lcm$的最大值
考虑枚举$gcd$的值,将所有$gcd$的倍数加入新序列内,问题转化为求序列中任意互质数对乘积的最大值
考虑维护一个栈,并将新序列中的元素从大到小依次加入,对于新加入的元素$x$,如果栈中存在$y$与其互质,那么栈中所有比$y$小的元素都可以删除,因为此时$x*y$一定大于接下来可能出现的答案$x’*y’$。($y’$是栈中比$y$小的数,而$x’$是接下来处理的元素,由于从大到小处理,所以$x’$一定小于$x$)所以当栈中存在与$x$互质的元素时,可以一直删除栈顶并更新答案
考虑如何查询栈中是否存在元素与$x$互质,设$cnt_d$表示栈中的元素有多少个是$d$的倍数,则经过简单的推导可知与$x$互质的数的个数为$\sum_{d|x} \mu(d)*cnt_d$。因此在栈中插入$x$与查询$x$的复杂度均为$O(d(i))$
以上两种算法结合,可以将判定是否存在互质对的问题转化为求最大乘积互质对的问题
因为每个数要入栈$d(i)$次,且每次入栈的复杂度小于$O(d(i))$,所以总复杂度为$O(\sum d(i)^2)$
代码
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5+1;
int n; int a[N],u[N],cnt[N]; vector<int>g[N],d[N]; ll ans;
void prepare(){ static bool vis[N]; static int p[N]; u[1]=1; for(int i=2;i<N;++i){ if(!vis[i]){ p[++p[0]]=i; u[i]=-1; } for(int j=1;j<=p[0]&&p[j]*i<N;++j){ vis[i*p[j]]=1; if(i%p[j]==0)break; else u[i*p[j]]=-u[i]; } } for(int i=1;i<N;++i) for(int j=1;i*j<N;++j) d[i*j].push_back(i); }
void add(int x,int c){ for(int i=0;i<d[x].size();++i){ cnt[d[x][i]]+=c; } }
bool ask(int x){ int ret=0; for(int i=0;i<d[x].size();++i){ ret+=cnt[d[x][i]]*u[d[x][i]]; } return ret>0; }
int main(){ prepare(); cin>>n; for(int i=1;i<=n;++i)cin>>a[i]; sort(a+1,a+n+1); for(int i=1;i<n;++i) if(a[i]==a[i+1]){ ans=max(ans,(ll)a[i]); } n=unique(a+1,a+n+1)-a-1; for(int i=1;i<=n;++i){ for(int j=0;j<d[a[i]].size();++j){ int x=d[a[i]][j]; g[x].push_back(a[i]/x); } } for(int i=1;i<N;++i){ static int stack[N]; static int top; while(top){ add(stack[top--],-1); } for(int j=(int)g[i].size()-1;~j;--j){ int x=g[i][j]; while(top&&ask(x)){ ans=max(ans,1ll*x*stack[top]*i); add(stack[top--],-1); } add(stack[++top]=x,1); } } cout<<ans<<endl; }
|