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

Posted AndyQsmart

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 = 100010;

LL d[2][maxN*2];

int lowbit(int x)
{
    return x&(-x);
}

void add(int t, int id,int pls)
{
    while(id <= maxN<<1)//id最大是maxN
    {
        d[t][id] += pls;
        id += lowbit(id);
    }
}

LL sum(int t, int to)
{
    LL s = 0;
    while(to > 0)
    {
        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-1);
}

int n, a[maxN], b[maxN];
LL k;

LL judge(int t)
{
    for (int i = 0; i < n; ++i)
    {
        if (a[i] > t) b[i] = 1;
        else if (a[i] == t) b[i] = 0;
        else b[i] = -1;
    }
    memset(d, 0, sizeof(d));
    int sum = 0;
    LL ans = 0;
    for (int i = 0; i < n; ++i)
    {
        sum += b[i];
        ans += query(!(i%2), 100005-n, 100005+sum-1);
        if (i%2 == 0 && sum > 0) ans++;
        add(i%2, 100005+sum, 1);
    }
    return ans;
}

void work()
{
    int lt, rt, mid;
    lt = rt = a[0];
    for (int i = 1; i < n; ++i)
    {
        lt = min(lt, a[i]);
        rt = max(rt, a[i]);
    }
    while ((LL)lt+1 < rt)
    {
        mid = ((LL)lt+rt)>>1;
        if (judge(mid) > k-1) lt = mid;
        else rt = mid;
    }
    if (judge(lt) <= k-1) 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 = 0; i < n; ++i) scanf("%d", &a[i]);
        work();
    }
    return 0;
}
View Code

 

以上是关于ACM学习历程—51NOD 1685 第K大区间2(二分 && 树状数组 && 中位数)的主要内容,如果未能解决你的问题,请参考以下文章

ACM学习历程—51NOD 1412 AVL树的种类(递推)

51nod 第K大区间2(二分+树状数组)

51nod 1686 第k大区间

51nod-1686 第K大区间(二分+尺取法)

51Nod——T 1686 第K大区间

ACM学习历程—51NOD 1770数数字(循环节)