CF1285F Classical?

本文探讨了在给定的正整数集合中寻找两两最小公倍数(LCM)的最大值的问题。通过枚举最大公约数(GCD),并利用容斥原理和栈的数据结构,提出了一种高效的算法解决方案,其复杂度约为O(nlog^2V)。

题意

给定 nnn 个正整数,求两两 lcm\text{lcm}lcm 的最大值。
所有输入在[1,105][1,10^5][1,105]范围内。

分析

首先枚举 gcd⁡=g\gcd = ggcd=g,那么将数组中 ggg 的倍数都拿出来。
问题转化为,在数组中找两个互质的数,使他们的积尽量大。
我们将这些数从大到小枚举,那么枚举到一个数 xxx 时,在已经枚举到的数中,如果有 x,yx,yx,y 互质,那么对于所有 z∈[x,y]z\in[x,y]z[x,y] 的数,都不可能形成最终答案。所以可以维护一个栈,对于当前 xxx,如果存在 yyyxxx 互质,就弹掉 xxxyyy 之间的数。
那怎么判断数组中是否有与 xxx 互质的数?
cntdcnt_dcntd 表示已经枚举的值中 ddd 的倍数的个数,tottottot 表示与 xxx 互质的数的个数。那么由容斥原理,tot=∑d∣xμ(d)×cntdtot=\sum\limits_{d|x}\mu(d)\times cnt_dtot=dxμ(d)×cntd
然后每次入栈和弹栈,都用 σ0(x)\sigma_0(x)σ0(x)xxx 的约数个数) 的时间来更新 cntcntcnt 数组。
总复杂度是 O(∑i=1nσ0(i)2)O(\sum\limits_{i=1}^{n}\sigma_0(i)^2)O(i=1nσ0(i)2) 的,大概是 O(nlog2V)O(nlog^2V)O(nlog2V)
因为每个数作为倍数都入过 σ0(x)\sigma_0(x)σ0(x) 次栈,每次入栈都要用 σ0(x)\sigma_0(x)σ0(x) 来更新 cntcntcnt 数组。

代码如下

#include <bits/stdc++.h>
#define N 100005
#define LL long long
using namespace std;
vector<int> d[N];
int mu[N], v[N], q[N], p[N], x[N], cnt[N], tot, y, maxn = N - 5;
LL t, z = 1;
int get(int x){
	int i, ans = 0;
	for(i = 0; i < d[x].size(); i++) ans += mu[d[x][i]] * cnt[d[x][i]];
	return ans;
}
void update(int x, int v){
	int i;
	for(i = 0; i < d[x].size(); i++) cnt[d[x][i]] += v;
}
int main(){
	int i, j, n, m, c;
	LL ans = 0;
	mu[1] = 1;
	for(i = 2; i <= maxn; i++){
		if(!x[i]) x[i] = p[++tot] = i, mu[i] = -1;
		for(j = 1; j <= tot; j++){
			t = z * i * p[j];
			if(t > maxn) break;
			x[t] = p[j];
			if(i % p[j] == 0) break;
			mu[t] = -mu[i];
		}
	}
	for(i = 1; i <= maxn; i++)
		for(j = 1; j <= maxn / i; j++) d[i * j].push_back(i);
	scanf("%d", &n);
	for(i = 1; i <= n; i++){
		scanf("%d", &j);
		v[j] = 1;
		ans = max(ans, z * j);
	}
	for(i = 1; i <= maxn; i++){
		for(j = maxn / i; j >= 1; j--){
			if(!v[i * j]) continue;
			c = get(j);
			while(c){
				if(__gcd(q[y], j) == 1){
					ans = max(ans, z * i * j * q[y]);
					c--;
				}
				update(q[y], -1);
				y--;
			}
			q[++y] = j;
			update(j, 1);
		}
		while(y > 0) update(q[y], -1), y--;
	}
	printf("%lld", ans);
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值