CF1446D Frequency Problem

发布时间 2023-12-19 21:02:55作者: cxqghzj

题意

给定 \(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;
}