CF1146D Frequency Problem
题意简述
给定一个长度为 \(n\) 的数列 \(\{a_i\}\),满足 \(1\le a_i\le n\)。
求一个最长的子区间,使得这个子区间内出现次数最多的数不止一个。
数据范围:\(1\le n\le 2\times10^5\)。
对于简单版有 \(1\le a_i\le 100\)。
Solution
看上去没办法用数据结构强行维护,考虑做一些转化。
考虑长度为 \(i\) 的前缀,设这个前缀存在唯一出现次数最多的数为 \(x\),令左端点 \(j\) 初始为 \(1\)。
在左端点 \(j\) 不断加 \(1\) 的过程中,维护 \([j,i]\) 内每种数的出现次数,一旦当前存在一个 \(y\ne x\) 满足 \(y\) 和 \(x\) 出现次数相等,就停止扫描,这时候可以用 \(i-j+1\) 更新答案。
设 \(s_{i,x,y}=\sum_{j=1}^i[a_j=x]-\sum_{j=1}^i[a_j=y]\),则对于右端点 \(i\),出现次数最多的数为 \(x\),其最小合法左端点为:
即对于每种不为 \(x\) 的数 \(y\) 都考虑其第一次出现次数相等的位置取个最小值。
于是可以枚举 \(x\) 和 \(y\),注意到我们一定有 \(a_{\text{minimum }j\text{ satisfying }s_{j,x,y}=s_{i,x,y}}=x\),故可以枚举所有 \(a_i=x\) 的位置 \(i\) 和所有满足 \([1,i]\) 中出现次数最多的数为 \(x\) 的位置 \(i\) 来求出 \(\text{minimum }j\text{ satisfying }s_{j,x,y}=s_{i,x,y}\),期间需要对每种 \(s_{i,x,y}\) 的出现位置记录一个数组,并且需要一些特殊技巧来达到 \(O(n)\) 的空间。
这样可以过 Easy。对于 Hard,取 \(S=\lfloor\sqrt n\rfloor\),把数分成出现次数 \(\le S\) 和 \(>S\) 两类,用上面的方法可以 \(O(nS)\) 求出所有满足最大出现次数 \(>S\) 的区间长度最大值。
对于最大出现次数 \(\le S\) 的区间,不难发现区间右端点和最大出现次数确定了之后,只需考虑最小的左端点,故这一部分外围枚举 \(1\le j\le S\) 表示最大出现次数,然后右端点 \(i\) 从小到大用 two-pointers 维护最小左端点即可,这一部分复杂度也是 \(O(nS)\),总复杂度 \(O(n\sqrt n)\)。
Code
#include <bits/stdc++.h>
template <class T>
inline void read(T &res)
{
res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
if (bo) res = ~res + 1;
}
template <class T>
inline T Max(const T &a, const T &b) {return a > b ? a : b;}
template <class T>
inline T Min(const T &a, const T &b) {return a < b ? a : b;}
const int N = 2e5 + 5;
int n, a[N], ans, cnt[N], ty[N], c[N], z[N], o[N], s[N], ToT, vis[N], fis[N],
S, b[N], id[N], tot;
std::vector<int> ot[N], occ[N];
void inc(int c, int i, int &cur1, int &cur2, int delta)
{
if (cnt[c] == i) cur1--; if (cnt[c] > i) cur2--;
cnt[c] += delta;
if (cnt[c] == i) cur1++; if (cnt[c] > i) cur2++;
}
int main()
{
int cur = 0, x;
read(n); S = sqrt(n);
for (int i = 1; i <= n; i++) read(a[i]), b[a[i]]++;
for (int i = 1; i <= n; i++) if (b[i] > S) id[i] = ++tot;
for (int i = 1; i <= n; i++)
{
if ((++cnt[a[i]]) > cur) cur = cnt[a[i]], x = a[i];
else if (cnt[a[i]] == cur) x = -1;
ty[i] = x; c[i] = cur; o[i] = cnt[a[i]];
if (x == -1) ans = i;
if (id[a[i]]) ot[id[a[i]]].push_back(-i);
if (x > 0 && id[x]) ot[id[x]].push_back(i);
}
for (int i = 1; i <= n; i++) z[i] = n + 1;
for (int T = 1; T <= tot; T++)
{
for (int i = 1; i <= n; i++) s[i] = s[i - 1] + (id[a[i]] == T);
for (int D = 1; D <= tot; D++) if (D != T)
{
ToT++;
for (int i = 0; i < ot[D].size(); i++)
if (ot[D][i] < 0)
{
int tmp = o[-ot[D][i]] - s[-ot[D][i]];
if (tmp >= 0 && vis[tmp] < ToT)
vis[tmp] = ToT, fis[tmp] = -ot[D][i];
}
else
{
int tmp = c[ot[D][i]] - s[ot[D][i]];
if (vis[tmp] == ToT)
z[ot[D][i]] = Min(z[ot[D][i]], fis[tmp]);
}
}
}
for (int i = 1; i <= n; i++) ans = Max(ans, i - z[i]);
for (int T = 1; T <= S; T++)
{
memset(cnt, 0, sizeof(cnt));
for (int i = 1, j = 1, cur1 = 0, cur2 = 0; i <= n; i++)
{
inc(a[i], T, cur1, cur2, 1);
while (cur2) inc(a[j], T, cur1, cur2, -1), j++;
if (cur1 > 1) ans = Max(ans, i - j + 1);
}
}
return std::cout << ans << std::endl, 0;
}