CF1446D2 Frequency Problem (Hard Version) 题解

Description

给出 nn 个元素组成的序列 a1,a2,…,ana_1,a_2,\ldots,a_n。

求最长的子段使得其中有至少两个出现次数最多的元素。

输出最长子段长度。

1≤n≤2×1051\leq n\leq 2\times 10^5。

Solution

首先有个关键性质是如果设全局的众数为 xx,则在最终的最长子段中一定有个众数是 xx。

证明就考虑如果 xx 不是当前子段的众数,则可以拓展左右端点,拓展一次不足以让 xx 变成唯一的众数,当拓展到 xx 刚好与之前的众数出现次数相等时这个子段就满足条件了,而由于 xx 是全局的众数,所以一定可以拓展到这个局面。

然后就可以自然地想到枚举另一个众数 yy,求出满足 xx 和 yy 出现次数相等的最长子段(不需要保证 xx 和 yy 出现次数最多),这么做显然是对的,因为根据上面那个做法,如果不是众数就可以再继续拓展直到 xx 是众数。

但是上面的做法会做颜色种类次,可能会很多。

考虑根号分治。

对于出现次数大于 n\sqrt n 的 yy 跑上面的做法。剩下的就一定满足众数出现次数不超过 n\sqrt n,则可以枚举众数出现次数 kk,对于每个 ll,找到满足众数出现次数不超过 kk 的最大 rr,然后判断出现次数恰为 rr 的数是否有至少两个。这个容易用双指针维护。

时间复杂度:O(nn)O(n\sqrt n)。

Code

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 2e5 + 5;

int n, lim, x, ans;
int a[kMaxN], cnt[kMaxN] = {0};

void solve_big(int y) {
static int sum[kMaxN], pos[kMaxN * 2];
std::fill_n(pos, 2 * n + 1, 1e9);
pos[n] = 0;
for (int i = 1; i <= n; ++i) {
sum[i] = sum[i - 1];
if (a[i] == x) ++sum[i];
else if (a[i] == y) --sum[i];
if (pos[sum[i] + n] == 1e9) pos[sum[i] + n] = i;
else ans = std::max(ans, i - pos[sum[i] + n]);
}
}

void solve_big() {
for (int i = 1; i <= n; ++i) {
if (cnt[i] > lim && i != x) {
solve_big(i);
}
}
}

void solve_small() {
for (int c = 1; c <= lim; ++c) {
static int cnt[kMaxN] = {0}, ccnt[kMaxN] = {0};
std::fill_n(cnt, n + 1, 0);
std::fill_n(ccnt, n + 1, 0);
ccnt[0] = n;

for (int l = 1, r = 0; l <= n; --ccnt[cnt[a[l++]]--]) {
for (; r < n && !ccnt[c + 1]; ++ccnt[++cnt[a[++r]]]) {}
if (ccnt[c + 1]) --ccnt[cnt[a[r--]]--];
assert(!ccnt[c + 1]);
if (ccnt[c] >= 2) ans = std::max(ans, r - l + 1);
}
}
}

void dickdreamer() {
std::cin >> n;
for (int i = 1; i <= n; ++i) {
std::cin >> a[i];
++cnt[a[i]];
if (cnt[a[i]] > cnt[x]) x = a[i];
}
lim = sqrtl(n);
solve_big(), solve_small();
std::cout << ans << '\n';
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

CF1446D2 Frequency Problem (Hard Version) 题解
https://sobaliuziao.github.io/2025/02/12/post/ac723bb6.html
作者
Egg_laying_master
发布于
2025年2月12日
许可协议