CF1446D Frequency Problem
题意
给定 \(n\) 个数。
求最长的子段使得子段内有两个众数。
Sol
考虑全局众数对于子段的众数的影响。
注意到对于答案有贡献的子段一定包含全局众数,读者自证不难。
考虑对于每个数出现的次数根号分治。
对于出现次数大于根号的数:
种类不超过根号。
考虑暴力对于每一种数,考虑她成为众数的情况。
具体地,将全局众数看为 \(+1\),她看为 \(-1\)。类似用前缀和的东西来判断。
对于出现次数小于根号的数:
考虑枚举出现的次数。
对于每种出现的次数跑双指针。
时间复杂度 \(O(n \sqrt n)\)
Code
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <array>
#include <vector>
using namespace std;
#ifdef ONLINE_JUDGE
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[1 << 23], *p1 = buf, *p2 = buf, ubuf[1 << 23], *u = ubuf;
#endif
int read() {
int p = 0, flg = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') flg = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
p = p * 10 + c - '0';
c = getchar();
}
return p * flg;
}
void write(int x) {
if (x < 0) {
x = -x;
putchar('-');
}
if (x > 9) {
write(x / 10);
}
putchar(x % 10 + '0');
}
const int N = 2e5 + 5;
array <int, N> s, h;
array <int, 2 * N> isl;
int main() {
int n = read(), pos = 0;
for (int i = 1; i <= n; i++) {
s[i] = read();
h[s[i]]++;
if (h[s[i]] > h[pos]) pos = s[i];
}
int ans = 0;
for (int i = 1; i <= n; i++) {
if (h[i] <= 450 || i == pos) continue;
int tp = 0;
for (int j = 1; j <= n; j++) {
if (s[j] == i) tp--;
if (s[j] == pos) tp++;
if (isl[tp + n] || !tp)
ans = max(ans, j - isl[tp + n]);
else
isl[tp + n] = j;
}
tp = 0;
for (int j = 1; j <= n; j++) {
if (s[j] == i) tp--;
if (s[j] == pos) tp++;
isl[tp + n] = 0;
}
}
for (int i = 1; i <= 450; i++) {
int tp = 1, res = 0; h.fill(0);
for (int j = 1; j <= n; j++) {
h[s[j]]++;
if (h[s[j]] == i) res++;
while (tp <= j && h[s[j]] > i) {
if (h[s[tp]] == i) res--;
h[s[tp]]--;
tp++;
}
if (res >= 2) ans = max(ans, j - tp + 1);
}
}
write(ans), puts("");
return 0;
}