CF1285F Classical?
Link
设\(A=\max(a_i)\)。
先枚举\(g\),然后计算满足\(\gcd(a_i,a_j)=g\)的答案。
考虑从大到小枚举集合中的数,假如现在枚举到\(x\),若存在\(y>x\)满足\(\gcd(x,y)=g\),那么所有\((x,y)\)内的数都不会对答案有贡献了。
因此我们可以弹栈直到栈中没有数与\(x\)互质,弹栈的时候计算贡献。
随之而来的问题就是如何计算栈中有多少与\(x\)互质的数,设\(cnt_x\)表示\(x\)的出现次数,考虑经典反演:
\(\sum\limits[\gcd(x,y)=1]cnt_y=\sum\limits_{d|x}\mu(d)\sum\limits_{d|y}cnt_y\)
那么我们维护\(cnt'_x=\sum\limits_{x|y}cnt_y\)即可。
这样做的时间复杂度为\(O(n\log^2n)\)。
那么我们最开始将\(a_i\)的所有因数都加入集合,然后只需要枚举\(\gcd(a_i,a_j)=1\)跑一遍即可得到答案。
最后记得特判\(\operatorname{lcm}(a_i,a_i)\)的情况。
时间复杂度为\(O(A\log A)\)。
#include<cctype>
#include<cstdio>
#include<vector>
#include<algorithm>
using i64=long long;
const int N=100007;
int vis[N],cnt[N],mu[N];std::vector<int>fac[N],stk;
int read(){int x=0,c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
int main()
{
int n=read(),m=0;i64 ans=0;mu[1]=1;
for(int i=1,x;i<=n;++i) m=std::max(m,x=read()),vis[x]=1;
for(int i=1;i<=m;++i) for(int j=i+i;j<=m;j+=i) mu[j]-=mu[i];
for(int i=1;i<=m;++i) for(int j=i;j<=m;j+=i) if(vis[i]|=vis[j],mu[i]) fac[j].push_back(i);
for(int i=m;i;--i)
{
int num=0;
if(!vis[i]) continue;
for(int j:fac[i]) num+=mu[j]*cnt[j];
for(;num;stk.pop_back())
{
int last=num;
for(int j:fac[stk.back()]) if(--cnt[j],!(i%j)) num-=mu[j];
if(last^num) ans=std::max(ans,1ll*i*stk.back());
}
for(int j:fac[i]) ++cnt[j];
stk.push_back(i);
}
printf("%lld",std::max(ans,1ll*m));
}