51nod1685 第K大区间2

该博客介绍了如何利用二分法和树状数组解决寻找所有长度为奇数区间中,第K大的中位数问题。通过输入n个数和目标值K,利用树状数组维护区间和,统计满足条件的区间数量,最终找到答案。
摘要由CSDN通过智能技术生成

Description

定义一个长度为奇数的区间的值为其所包含的的元素的中位数。中位数_百度百科 

现给出n个数,求将所有长度为奇数的区间的值排序后,第K大的值为多少。

 

样例解释:


[l,r]表示区间的值
[1]:3
[2]:1
[3]:2
[4]:4
[1,3]:2
[2,4]:2


第三大是2

Input

第一行两个数n和k(1<=n<=100000,k<=奇数区间的数量)
第二行n个数,0<=每个数<2^31

Output

一个数表示答案。

Input示例

4 3
3 1 2 4

Output示例

2

解题思路:(二分+树状数组)

区间第k大首先想到二分求中位数>=x的区间有多少个,所以快速求满足条件的区间才是关键,若大于等于x则1,否则 - 1;等到一个数组,求任意区间和>= 0 的奇数长度区间的个数即可,用两个数组数组分别维护区间和,在他前面和<=当前和的小标奇偶性不同的区间总个数,统计一下即可。

Code
#include<stdio.h>
#include<string.h>
#define MAXN 100000 + 10
#define INF (1<<30) - 1 + (1 << 30)
int a[MAXN];
int jtree[2 * MAXN];//奇数
int otree[2 * MAXN];//偶数
int lowbit(int x)
{
    return x & (-x);
}
void jAdd(int k)
{
    while(k < 2 *MAXN)
    {
        jtree[k] ++;
        k += lowbit(k);
    }
}


int jSum(int k)
{
    int sum = 0;
    while(k)
    {
        sum += jtree[k];
        k -= lowbit(k);
    }
    return sum;
}
void oAdd(int k)
{
    while(k < 2 *MAXN)
    {
        otree[k] ++;
        k += lowbit(k);
    }
}


int oSum(int k)
{
    int sum = 0;
    while(k)
    {
        sum += otree[k];
        k -= lowbit(k);
    }
    return sum;
}
int main()
{
    int n;
    long long k;
    scanf("%d%lld",&n,&k);
    for(int i = 0; i < n; i++)
        scanf("%d",a+i);
    long long l = 0,r = INF,mid;
    while(l < r)
    {
        memset(jtree,0,sizeof(jtree));
        memset(otree,0,sizeof(otree));
           // jAdd(MAXN);
        mid = (r + l + 1) / 2;
       // printf("%d...\n",mid);
        long long sum = 0,ans = 0;
        for(int i = 0; i < n; i ++)
        {
            if(a[i] >= mid)
            {
                sum ++;
            }
            else
                sum--;
            if(i % 2 == 0)
            {
                ans += jSum(sum + MAXN -1);
                oAdd(sum + MAXN);
                if(sum > 0)
                    ans ++;
            }
            else
            {
                ans += oSum(sum + MAXN - 1);
                jAdd(sum + MAXN);
            }
        }
        if(ans >= k)
            l = mid;
        else
            r = mid - 1;
    }
    printf("%lld\n",l);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值