ACM学习历程—51NOD 1685 第K大区间2(二分 && 树状数组 && 中位数)

时间:2023-03-08 22:02:02
ACM学习历程—51NOD 1685 第K大区间2(二分 && 树状数组 && 中位数)

http://www.51nod.com/contest/problem.html#!problemId=1685

这是这次BSG白山极客挑战赛的E题。

这题可以二分答案t。

关键在于,对于一个t,如何判断它是否能成为第k大。

将序列中大于t的置为1,小于t的置为-1,等于t的置为0。那么区间中位数大于t的和就大于0,小于t的就小于0。于是就是判断区间和大于0的个数是否小于等于k。

维护前缀和sum(i),然后统计之前sum(j)小于sum(i)的有多少个,就是以i为右值的区间和大于0的个数。于是就可以用树状数组维护了。

由于是奇数长度区间,所以树状数组需要维护奇偶长度的前缀和个数。需要特判sum(i) > 0的情况。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
#include <vector>
#include <string>
#define LL long long using namespace std; //线段树
//区间每点增值,求区间和
const int maxN = ; LL d[][maxN*]; int lowbit(int x)
{
return x&(-x);
} void add(int t, int id,int pls)
{
while(id <= maxN<<)//id最大是maxN
{
d[t][id] += pls;
id += lowbit(id);
}
} LL sum(int t, int to)
{
LL s = ;
while(to > )
{
s = s + d[t][to];
to -= lowbit(to);
}
return s;
} LL query(int t, int from, int to)
{
return sum(t, to) - sum(t, from-);
} int n, a[maxN], b[maxN];
LL k; LL judge(int t)
{
for (int i = ; i < n; ++i)
{
if (a[i] > t) b[i] = ;
else if (a[i] == t) b[i] = ;
else b[i] = -;
}
memset(d, , sizeof(d));
int sum = ;
LL ans = ;
for (int i = ; i < n; ++i)
{
sum += b[i];
ans += query(!(i%), -n, +sum-);
if (i% == && sum > ) ans++;
add(i%, +sum, );
}
return ans;
} void work()
{
int lt, rt, mid;
lt = rt = a[];
for (int i = ; i < n; ++i)
{
lt = min(lt, a[i]);
rt = max(rt, a[i]);
}
while ((LL)lt+ < rt)
{
mid = ((LL)lt+rt)>>;
if (judge(mid) > k-) lt = mid;
else rt = mid;
}
if (judge(lt) <= k-) printf("%d\n", lt);
else printf("%d\n", rt);
} int main()
{
//freopen("test.in", "r", stdin);
while (scanf("%d", &n) != EOF)
{
cin >> k;
for (int i = ; i < n; ++i) scanf("%d", &a[i]);
work();
}
return ;
}