CF1285F Classical
存在一个想法,搞一下迪利克雷前缀和在卷一下再逆操作一下就行了,但是由于相乘之后的数量级达到了 10^10 ,不太能这么搞。
还存在一个想法是枚举 gcd ,然后我们将 gcd 的倍数都提取出来,然后我们的任务就是在这些数中快速找到两个互质并且乘积最大的数。
依旧存在一个想法就是,如果我们从大到小扫描,如果能支持一个数快速判断一个集合中是否存在有数与其互质,那么我们便可以利用栈来解决这个问题,具体的我们考虑每次扫描到一个数时,考虑栈中是否存在有数与其互质,如果没有,那么就直接塞入,如果有,我们就一直弹出,找到最大的那个互质的数,更新答案。
问题现在是我们如何快速判断一个集合中的数是否存在和一个数互质的数,这个考虑莫反直接做,因为我们只需要获取 \(1\) 位置所在的值。复杂度的话,应该是两只 log 的,具体考虑每一个数会被计算几次。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,A=1e5+1;
int n,a[N],b[A],mu[A],f[A];
vector<int> bag[N],BAG;stack<int> s;
void init(){
vector<int> pri;bool tag[A];
memset(tag,0,sizeof(tag)),mu[1]=1;
for(int i=2;i<A;++i){
if(!tag[i]) pri.push_back(i),mu[i]=-1;
for(int j=0;j<(int)pri.size()&&i*pri[j]<A;++j){
tag[i*pri[j]]=true;
if(i%pri[j]==0){mu[i*pri[j]]=0;break;}
else mu[i*pri[j]]=mu[i]*-1;
}
}
}
long long res=0;
int main(){
cin>>n,init();
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
// for(int i=1;i<=20;++i) printf("%d ",mu[i]);
// printf("\n");
for(int i=1;i<=n;++i) b[a[i]]++;
for(int i=1;i<A;++i){
for(int j=1;i*j<A;++j)
bag[i*j].push_back(i);
}
for(int gcd=1;gcd<A;++gcd){
BAG.clear();
for(int i=1;gcd*i<A;++i){
for(int j=1;j<=b[gcd*i];++j)
BAG.push_back(i);
}
for(int i=(int)BAG.size()-1;i>=0;--i){
int tmp=0,lst=-1;
for(int j=0;j<(int)bag[BAG[i]].size();++j)
tmp+=f[bag[BAG[i]][j]]*mu[bag[BAG[i]][j]];
while(tmp){
lst=s.top(),s.pop(),tmp=0;
for(int j=0;j<(int)bag[lst].size();++j) f[bag[lst][j]]--;
for(int j=0;j<(int)bag[BAG[i]].size();++j)
tmp+=f[bag[BAG[i]][j]]*mu[bag[BAG[i]][j]];
}
if(lst>0) res=max(res,1ll*gcd*BAG[i]*lst);
else{
s.push(BAG[i]);
for(int j=0;j<(int)bag[BAG[i]].size();++j) f[bag[BAG[i]][j]]++;
}
}
while(!s.empty()){
int lst=s.top();s.pop();
for(int j=0;j<(int)bag[lst].size();++j) f[bag[lst][j]]--;
}
}
return printf("%lld\n",res),0;
}