CF1676F Longest Strike 题解
题意
给你一个长度为 $n$ 的序列 $a$ 和一个整数 $k$,你要求一个区间 $[l,r]$ 满足:
- 对于任何整数 $x∈[l,r]$,$x$ 在 $a$ 中的出现次数不少于 $k$ 次。
- 最大化 $r-l$。
无解输出 -1
。
思路
看到出现次数,先把 $a$ 装进桶 $c$ 里,
发现装不进去,就离散化一下再装进桶 $c$ 里。
令 $l,r$ 存正在处理的区间,$x,y$ 存目前最大的区间,$s$ 存目前最大区间的大小。
接着按离散化后的 $a_i$ 从小到大扫一遍:
- 如果 $c_i≥k$,区间可以包含 $a_i$,则 $r\gets a_i$,
- 如果 $a_i≠a_{i-1}+1$,不能与之前的区间接上,则“另起炉灶”,$l\gets a_i$。
- 否则 $l$ 保持原值不变。
- 用 $l,r$ 更新 $x,y,s$。
- 否则区间不能包含 $a_i$,$l\gets a_{i+1},r\gets a_{i+1}$。
注意这里 $a_1$ 必须“另起炉灶”,因为 $a_1$ 之前没有别的区间。
所以我们要手动让 $a_1≠a_0+1$,$a_0\gets-1$ 即可。
最后如果 $s$ 从未被更新过就是无解,否则解为 $[x,y]$。
代码
#include <cstdio>
#include <algorithm>
#define h(x) lower_bound(a + 1, a + m + 1, x) - a
using namespace std;
int T, n, m, k, s, l, r, x, y, a[200050], b[200050], c[200050];
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &n, &k);s = -1;
for(int i = 1;i <= n;++i) scanf("%d", &a[i]), b[i] = a[i];
sort(a + 1, a + n + 1);m = unique(a + 1, a + n + 1) - a - 1;
for(int i = 1;i <= m;++i) c[i] = 0;
for(int i = 1;i <= n;++i) ++c[h(b[i])];a[0] = -1;
for(int i = 1;i <= m;++i)
if(c[i] >= k)
{
if((r = a[i]) != a[i - 1] + 1) l = a[i];
if(r - l > s) s = r - l, x = l, y = r;
}
else l = r = a[i + 1];
if(s == -1) puts("-1");
else printf("%d %d\n", x, y);
}
return 0;
}