51nod 1685 第 K 大区间 2

题目大意:定义一个长度为奇数的区间的值为其所包含的的元素的中位数。现给出$n$个数的集合$a$,求将所有长度为奇数的区间的值排序后,第$K$大的值为多少。

思路:

考场上的我没有思路,于是弱智的我只会$60pts$主席树。

正解:

发现将数列排序后,所求的答案具有单调性。于是可以二分答案。

但是如何很快的求出一个区间的中位数呢?

我们可以将数列里大于当前二分查找的值的数置为1,等于的置为0,小于的置为-1。

这样,一个区间的和如果是0,中位数就是二分查找的数。

所以用树状数组维护区间和就好了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lowbit(x) x&-x
using namespace std;
int c[500005][2];
int a[500005],b[500005],d[500005];
int sum[500005];
int del=100005;
int n,m;
void add(int x,int num,int opt)
{
    int i;
    for(i=x;i<=500000;i+=lowbit(i))
        c[i][opt]+=num;
}
int query(int x,int opt)
{
    int sum=0;
    for(int i=x;i;i-=lowbit(i))
        sum+=c[i][opt];
    return sum;
}
int check(int k)
{
    memset(c,0,sizeof(c));
    int i;
    int num1=0,num2=0;
    add(del,1,0);
    for(i=1;i<=n;i++)
    {
        if(a[i]<k)
            d[i]=-1;
        else if(a[i]==k)
            d[i]=0;
        else
            d[i]=1;
        sum[i]=sum[i-1]+d[i];
        add(sum[i]+del,1,i%2);
        num1+=query(sum[i]+del,(i%2)^1)-query(sum[i]+del-1,(i%2)^1);
        num2+=query(sum[i]+del-1,(i%2)^1);
    }
    if(m>num1+num2)
        return 1;
    else if(m<num2)
        return 0;
    return 2;
}
int main()
{
    freopen("Kth.in","r",stdin);
    freopen("Kth.out","w",stdout);
    scanf("%d%d",&n,&m);
    int i,j,l,r;
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]),b[i]=a[i];
    sort(b+1,b+n+1);
    l=1;
    r=n;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        int tmp=check(b[mid]);
        if(tmp==1)
            r=mid-1;
        else if(tmp==0)
            l=mid+1;
        else
        {
            cout<<b[mid];
            break;
        }
    }
}
原文地址:https://www.cnblogs.com/handsome-wjc/p/11291322.html