51nod 1685 第K大区间2

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

现给出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大。

题解:二分答案
如果二分第k大为t,寻找大于t的区间的个数。设s[i]为1--i大于t的数的个数。
如果一个区间的中位数大于t,那么
s[r]-s[l-1]>(r-l+1)/2,整理一下
2*s[r]-r>2*s[l-1]-(l-1),
用树状数组统计一下即可。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
#define maxn 100009
using namespace std;

int n,l,r,mid,ans,a[maxn],b[maxn],tree[maxn*3][3];
LL k;

void add(int pos,int p){
    if(pos<=0)return;
    for(;pos<=maxn*3;pos+=pos&(-pos))tree[pos][p]++;
}

LL getsum(int pos,int p){
    LL all=0;if(pos<=0)return 0;
    for(;pos;pos-=pos&(-pos))all+=tree[pos][p];
    return all;
}

bool check(int p){
    LL s=0;
    memset(tree,0,sizeof(tree));
    for(int i=1;i<=n;i++)b[i]=a[i]>=p?1:0,b[i]+=b[i-1];
    for(int i=1;i<=n;i++)b[i]=2*b[i]-i+n;
    add(n,0);//???
    for(int i=1;i<=n;i++){
        s+=getsum(b[i],(i&1)^1);
        add(b[i],(i&1));
    }
    return s>=k;
}

int main(){
    scanf("%d%lld",&n,&k);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),r=max(r,a[i]);
    while(l<=r){
        mid=(l+r)>>1;
        if(check(mid))ans=mid,l=mid+1;
        else r=mid-1;
    }
    printf("%d
",ans);
    return 0;
}
AC

 
原文地址:https://www.cnblogs.com/zzyh/p/7650477.html