描述
题解
十分有意思的二分,二分第k大的数是多少,设为mid
,然后cala(mid)
,只要计算出有多少个区间的众数的次数是>= mid
的即可。使用尺取法+二分可以实现O(nlogn)
复杂度的算法。这里有一个暗藏的坑,k <= n * (n - 1) / 2
,说明区间的宽度最少为2,写mul(x)
函数时注意即可。
代码
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 10;
int n, a[MAXN], b[MAXN];
ll mul(ll x)
{
if (x <= 0)
{
return 0;
}
return x * (x - 1) / 2;
}
ll cala(int x)
{
ll ret = 0;
int i, l = 0, la = 0;
memset(b, 0, sizeof(b));
for (i = 1; i <= n; i++)
{
b[a[i]]++;
if (b[a[i]] > x)
{
ret += mul(i - l - 1);
ret -= mul(la - l - 1);
while (b[a[i]] > x)
{
l++;
b[a[l]]--;
}
la = i;
}
}
ret += mul(i - l - 1);
ret -= mul(la - l - 1);
return ret;
}
int main()
{
int i, l, r, mid;
ll k;
scanf("%d%lld", &n, &k);
for (i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
b[i] = a[i];
}
// 排序去重
sort(b + 1, b + n + 1);
r = (int)(unique(b + 1, b + n + 1) - (b + 1));
for (i = 1; i <= n; i++)
{
a[i] = (int)(lower_bound(b + 1, b + r + 1, a[i]) - b);
}
k = mul(n) - k + 1;
l = 0;
r = n;
mid = (l + r) / 2;
while (l + 1 < r)
{
if (cala(mid) < k)
{
l = mid;
mid = (l + r) / 2;
}
else
{
r = mid;
mid = (l + r) / 2;
}
}
printf("%d\n", r);
return 0;
}