P8330 [ZJOI2022] 众数 题解
Description
给定一个长度为
Solution
考虑根号分治。
定义一个大的颜色指出现次数
首先考虑两个众数中至少一个是大颜色的贡献。
假设大颜色为
第一种情况是形如
容易发现最优情况一定满足选定区间的左右端点都是
对于
然后考虑 小-小 的情况。
这里先枚举区间外的众数
这时只要枚举这些
但是这样要求
观察到这里只需要考虑
于是可以依次枚举
注意到这里
然后右端点为
容易发现一次更新至少会使
总的时间复杂度:
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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步