Codeforces1285F Classical?

Description

给定长度为 n 的数列 a,求

max1i<jn{LCM(ai,aj)}

n,ai105

Solution

LCM 先转化成乘积除以 gcd 的形式,枚举 gcd=t 并提取出来所有是该数倍数的数,下面叙述中的 x 都表示除过 t 得到的结果

从小到大枚举每个元素并将其设为 max{ai,aj} ,也就是说在被提取出来的所有数字中找到的最大的小于它的且和它互质的数

如果想要判定一些数字和某个数字 x 是不是互质,可以通过 [gcd(x,y)=1]=d|x,d|yμ(d) 来处理:

cntx 表示 x 的倍数在被提取出来的数字除 t 出现了多少次

先计算 s=d|xμ(x)cntx 再二分找到最大的前缀满足只考虑该前缀在桶里面的贡献仍然可以得到 s

不难发现还要把桶可持久化所以复杂度太高了

通过从大到小枚举来减少冗余计算量,如果某个 x 找到最大的 z 满足 gcd(x,z)=1 ,那么 x<y<z 的所有 y 在后续计算中将不被需要

那么维护一个栈以及和上面类似的桶,先算一次 d|xμ(x)cntx 得到互质元素数量,再直接弹栈到不再存在互质元素即可

复杂度到了 Θ(nlog2n) 但是还可以更优!

对于最优解 (x,y) 那么一定存在一组 a|x,b|ygcd(a,b)=1,lcm(a,b)=lcm(x,y) 那么直接将每个数字的存在性表示为其所有倍数存在性的 或 结果,直接做 gcd=1 的部分即可

时间复杂度 Θ(nlogn)

Code

const int N=1e5+10;
int stk[N],top;
int n;
bool a[N],isc[N];
int mu[N],pri[N],cnt;
vector<int> Div[N];
int ton[N];
signed main(){
	n=1e5; mu[1]=1;
	for(int i=2;i<=n;++i){
		if(!isc[i]) pri[++cnt]=i,mu[i]=-1;
		for(int j=1;j<=cnt&&pri[j]*i<=n;++j){
			isc[i*pri[j]]=1;
			if(i%pri[j]==0){
				mu[i*pri[j]]=0;
				break;
			}else mu[i*pri[j]]=-mu[i];
		}
	}
	n=read();
	for(int i=1;i<=n;++i) a[read()]=1;
	for(int i=1e5;i>=1;--i){
		for(int j=i;j<=1e5;j+=i){
			a[i]|=a[j];
			Div[j].emplace_back(i);
		}
	}
	int ans=0;
	for(int i=1e5;i>=1;--i) if(a[i]){
		ckmax(ans,i);
		int num=0;
		for(auto t:Div[i]) num+=ton[t]*mu[t];
		while(num){
			if(gcd(i,stk[top])==1) --num,ckmax(ans,i*stk[top]);
			for(auto t:Div[stk[top]]) --ton[t];
			--top;
		}
		for(auto t:Div[i]) ton[t]++;
		stk[++top]=i;
	}
	print(ans);
	return 0;
}
posted @   没学完四大礼包不改名  阅读(58)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示