P8330 [ZJOI2022] 众数 题解

Description

给定一个长度为 nn 的序列 a1,a2,,ana_1,a_2,\dots,a_n,问选一段区间,它的价值是里面的众数个数 ++ 外面的众数个数,求最大价值,以及所有满足这个最大价值的区间的外面的众数颜色。

n5×105,n2×105\sum n\leq 5\times 10^5,n\leq 2\times 10^5

Solution

考虑根号分治。

定义一个大的颜色指出现次数 >n>\sqrt n 的颜色,小的颜色指出现次数 n\leq\sqrt n 的颜色。

首先考虑两个众数中至少一个是大颜色的贡献。

假设大颜色为 xx,另一个为 yy

第一种情况是形如 xxxxyyyyyxxxx\texttt{xxxxyyyyyxxxx}xx 在两边,yy 在中间的贡献。这时会发现一个区间 [l,r][l,r] 的答案就是 xx 的出现次数 ++ [l,r][l,r]yy 的出现次数 - [l,r][l,r]xx 的出现次数,这个东西很容易搞成区间和的形式,搞个最大子段和即可做到单次 O(n)O(n)

容易发现最优情况一定满足选定区间的左右端点都是 yy,所以只要对于所有颜色为 yy 的位置计算答案即可做到单次 O(cnty)O(cnt_y)

对于 yyyyxxxxxyyyy\texttt{yyyyxxxxxyyyy} 的情况是同理的,所以这一部分的总时间复杂度为 O(n)O(\sqrt n)


然后考虑 小-小 的情况。

这里先枚举区间外的众数 xx,容易发现最优情况的区间 [l,r][l,r] 一定满足:l=1l=1al1=xa_{l-1}=x,和 r=nr=nar+1=xa_{r+1}=x

这时只要枚举这些 [l,r][l,r] 再求出 [l,r][l,r] 之间的众数即可得出答案。

但是这样要求 O(nn)O(n\sqrt n) 次区间众数,显然过不了。

观察到这里只需要考虑 [l,r][l,r] 中出现次数 n\leq \sqrt n 的数的众数,所以 [l,r][l,r] 的答案也 n\leq \sqrt n

于是可以依次枚举 rr,维护 sls_l 表示 [l,r][l,r] 的众数。然后对于所有 rr 前且颜色和 rr 相等的位置 ii,需要对于所有 jij\leq isjs_j[i,r][i,r]aia_i 的个数取 max\max

注意到这里 ss 一定是单调不增的,所以只需从后往前扫,如果能更新就更新,否则就退出。

然后右端点为 r+1r+1 的答案即为 maxci+cnt[1,i1],ar+1+cnt[r+1,n],ar+1\max{c_i+cnt_{[1,i-1],a_{r+1}}+cnt_{[r+1,n],a_{r+1}}},这个暴力枚举颜色与 r+1r+1 相同的 ii 即可。

容易发现一次更新至少会使 ss 的总和 +1+1,而最终 sins_i\leq \sqrt n,所以时间复杂度就是 O(nn)O(n\sqrt n)

总的时间复杂度: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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 2e5 + 5;

int n, m, b;
int a[kMaxN], unq[kMaxN], pos[kMaxN], res[kMaxN], sum[kMaxN];
std::vector<int> v[kMaxN];

void discrete() {
b = sqrt(n);
for (int i = 1; i <= n; ++i)
unq[i] = a[i];
std::sort(unq + 1, unq + 1 + n);
m = std::unique(unq + 1, unq + 1 + n) - (unq + 1);
for (int i = 1; i <= n; ++i)
v[i].clear(), res[i] = 0;
for (int i = 1; i <= n; ++i) {
a[i] = std::lower_bound(unq + 1, unq + 1 + m, a[i]) - unq;
pos[i] = v[a[i]].size();
v[a[i]].emplace_back(i);
}
}

int big1(int x, int y) { // xxxxxyyyyyyyyyxxxxx
static int pre1[kMaxN], pre2[kMaxN];
int mi = 1e9, ret = 0;
for (int i = 0; i < (int)v[y].size(); ++i) {
pre1[i] = sum[v[y][i]] + i + 1;
pre2[i] = sum[v[y][i] - 1] + i;
mi = std::min(mi, pre2[i]);
ret = std::max(ret, pre1[i] - mi);
}
return ret + v[x].size();
}

int big2(int x, int y) { // yyyyyxxxxxxyyyyyy
static int pre[kMaxN], suf[kMaxN], mx[kMaxN];
for (int i = 0; i < (int)v[y].size(); ++i) {
pre[i] = sum[v[y][i]] + i + 1;
suf[i] = sum[n] - sum[v[y][i] - 1] + v[y].size() - i;
mx[i] = (i == 0 ? pre[i] : std::max(mx[i - 1], pre[i]));
}
int now = -1e9, ret = mx[(int)v[y].size() - 1];
for (int i = (int)v[y].size() - 1; i; --i) {
now = std::max(now, suf[i]);
ret = std::max(ret, now + pre[i - 1]);
}
ret = std::max(ret, now);
return ret + v[x].size();
}

void solve_big() {
for (int x = 1; x <= m; ++x) {
if (v[x].size() <= b) continue;
for (int i = 1; i <= n; ++i)
sum[i] = sum[i - 1] - (a[i] == x);
for (int y = 1; y <= m; ++y) {
res[x] = std::max(res[x], big1(x, y));
res[y] = std::max(res[y], big2(x, y));
}
}
}

void solve_small() {
static int c[kMaxN], cnt[kMaxN];
// c[i] : [i, r] 的众数
std::fill_n(c + 1, n, 0);
std::fill_n(cnt + 1, n, 0);
for (int i = 1; i <= n; ++i) {
if (v[a[i]].size() > b) continue;
res[a[i]] = std::max(res[a[i]], c[1] + (int)v[a[i]].size() - pos[i]);
for (int j = pos[i] - 1; ~j; --j) {
res[a[i]] = std::max(res[a[i]], c[v[a[i]][j] + 1] + (int)v[a[i]].size() - pos[i] + j + 1);
}
for (int j = pos[i]; ~j; --j) {
for (int k = v[a[i]][j]; k && c[k] <= pos[i] - j + 1; --k)
c[k] = pos[i] - j + 1;
}
}
int mx = 0;
for (int i = n; i; --i) {
res[a[i]] = std::max(res[a[i]], mx + pos[i] + 1);
mx = std::max(mx, ++cnt[a[i]]);
}
}

void dickdreamer() {
std::cin >> n;
for (int i = 1; i <= n; ++i)
std::cin >> a[i];
discrete(), solve_big(), solve_small();
int mx = 0;
for (int i = 1; i <= m; ++i) mx = std::max(mx, res[i]);
std::cout << mx << '\n';
for (int i = 1; i <= m; ++i)
if (res[i] == mx)
std::cout << unq[i] << '\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;
}

P8330 [ZJOI2022] 众数 题解
https://sobaliuziao.github.io/2024/02/06/post/7fe200d8.html
作者
Egg_laying_master
发布于
2024年2月6日
许可协议