CF 1285F Classical?

题目描述

https://codeforces.com/problemset/problem/1285/F

简要题意:给定一个长度为 n 的序列 ai,求 max1i<jnlcm(ai,aj)

n,ai105

Solution

注意到 ai 的值域很小,我们考虑一个针对值域的做法,首先我们把所有 ai 的约数都求出来,令其为集合 S,那么最终的答案一定为从 S 中选出两个互质的数的最大 lcm,我们考虑二分 lcm,那么如果 xSyS[(x,y)=1][xylcm],按照莫比乌斯反演的套路,我们得到 dSμ(d)d|xd|y[xylcm],我们枚举 d,后面枚举 x 之后是一个双指针,总时间复杂度为 O(nlog2n)

其实这题还有一个 O(nlogn) 的做法,我们还是考虑对 Slcm,我们维护一个栈,从大到小加入数 x,如果栈中存在一个 yx 互质,那么对于 x<z<yz 一定没用了,因为我们现在的答案至少是 xy,而后面加入的数 wz 的答案至多是 wz<xy,所以我们可以一直弹栈顶直到栈中不存在与 x 互质的数,那么我们如何判断是否存在于 x 互质的数呢?答案还是莫反,我们有 yS[(x,y)=1]=d|xμdcntd,其中 cntd 表示有多少数是 d 的倍数,这个东西可以在入栈和出栈时顺便维护,时间复杂度 O(nlogn)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream> // 单 log
#include <vector>
#include <stack>
#include <algorithm>
#include <numeric>
#define maxn 100010
#define ll long long
using namespace std;

int pri[maxn], mu[maxn]; bool isp[maxn]; vector<int> D[maxn];
void init_isp(int n) {
mu[1] = 1; int cnt = 0;
for (int i = 2; i <= n; ++i) {
if (!isp[i]) pri[++cnt] = i, mu[i] = -1;
for (int j = 1; j <= cnt && i * pri[j] <= n; ++j) {
isp[i * pri[j]] = 1;
if (i % pri[j] == 0) break;
mu[i * pri[j]] = -mu[i];
}
}
for (int i = 1; i <= n; ++i)
for (int j = i; j <= n; j += i) D[j].push_back(i);
}

int n, cnt[maxn];
bool vis[maxn];

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);

cin >> n; vector<int> a;
for (int i = 1, x; i <= n; ++i) cin >> x, vis[x] = 1;
n = 100000;
for (int i = 1; i <= n; ++i) {
for (int j = i; j <= n; j += i) vis[i] |= vis[j];
if (vis[i]) a.push_back(i);
} sort(a.begin(), a.end(), greater<int>());
n = *max_element(a.begin(), a.end()); init_isp(n); stack<int> S; ll ans = 1;
for (auto x : a) {
int res = 0;
for (auto d : D[x]) res += mu[d] * cnt[d];
while (!S.empty() && res) {
int y = S.top(); S.pop();
for (auto d : D[y]) {
if (x % d == 0) res -= mu[d];
--cnt[d];
} if (gcd(x, y) == 1) ans = max(ans, 1ll * x * y);
}
for (auto d : D[x]) ++cnt[d]; S.push(x);
} cout << ans << "\n";
return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream> // 双 log
#include <vector>
#define maxn 100010
#define ll long long
using namespace std;

int pri[maxn], cnt, mu[maxn]; bool isp[maxn];
void init_isp(int n) {
mu[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!isp[i]) pri[++cnt] = i, mu[i] = -1;
for (int j = 1; j <= cnt && i * pri[j] <= n; ++j) {
isp[i * pri[j]] = 1;
if (i % pri[j] == 0) break;
mu[i * pri[j]] = -mu[i];
}
}
}

int n, a[maxn];

vector<int> A[maxn]; bool vis[maxn];
int check(ll x) {
ll ans = 0;
for (int i = 1; i <= n; ++i) {
if (!vis[i] || !mu[i]) continue; int m = A[i].size(); ll res = 0;
for (int j = 0, k = m - 1; j < m; ++j) {
while (k > 0 && 1ll * A[i][k - 1] * A[i][j] >= x) --k;
if (1ll * A[i][k] * A[i][j] >= x) res += m - k;
} ans += mu[i] * res;
} return ans > 0 ;
}

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);

cin >> n;
for (int i = 1, x; i <= n; ++i) cin >> x, vis[x] = 1;
n = 100000; init_isp(n);
for (int i = 1; i <= n; ++i)
for (int j = i; j <= n; j += i) vis[i] |= vis[j];
for (int i = 1; i <= n; ++i)
for (int j = i; j <= n; j += i) if (vis[j]) A[i].push_back(j);
ll l = 1, r = 1e10, mid, ans;
while (l <= r) {
mid = l + r >> 1;
if (check(mid)) ans = mid, l = mid + 1;
else r = mid - 1;
} cout << ans << "\n";
return 0;
}