欢迎来到下蛋爷之家|

下蛋爷

园龄:4年2个月粉丝:8关注:23

P8330 [ZJOI2022] 众数 题解

Description

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

n5×105,n2×105

Solution

考虑根号分治。

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

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

假设大颜色为 x,另一个为 y

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

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

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


然后考虑 小-小 的情况。

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

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

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

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

于是可以依次枚举 r,维护 sl 表示 [l,r] 的众数。然后对于所有 r 前且颜色和 r 相等的位置 i,需要对于所有 jisj[i,r]ai 的个数取 max

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

然后右端点为 r+1 的答案即为 maxci+cnt[1,i1],ar+1+cnt[r+1,n],ar+1,这个暴力枚举颜色与 r+1 相同的 i 即可。

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

总的时间复杂度:O(nn)

Code

#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;
}
posted @   下蛋爷  阅读(15)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起