【数据结构】归并树—区间第k大

求区间第k大有很多种方法比如可持久化线段树,分桶法和平方分割 当然,我们还可以用归并树;(请原谅蒟蒻才晓得归并树,写的不好)。
我们把数列用线段树维护起来。线段树的每个节点都保存了对应区间排好序后的结果。在这之前我接触到的线段树上面保存的都是数值,而这次有所不同,每个节点保存的是数列。
如图(画的丑,勿喷):
这里写图片描述
建立线段树的过程和归并排序类似,每个节点的数列是其两个儿子的数列进行排序的结果。建树的复杂度是O(nlogn);其实这棵树也就是归并排序的完整体现。
那么要找区间第k大,我们通过二分答案来判断。就像分桶法和平方分割 一样,只是判定方式有区别。
要找区间内比当前x小的数的个数,我们可以用这棵树来查找。
- 如果所给的区间和当前节点的区间没有交集,返回0;
- 如果所给的区间完全包含了当前区间,那么用二分查找的方法来对当前节点上保存的数列进行查找(比如可以用upper_bound);
- 否则对两个儿子进行递归计算后求和返回;
由于对于线段树需要logn个区间,每个区间查找需要logn次,查找一次的时间复杂的为O((logn)^2);
所以总的时间复杂度是O(nlogn+m(logn)^3);

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define lch (rt<<1)
#define rch ((rt<<1)+1)
const int MAXN=100005;
int a[MAXN],nums[MAXN];
vector<int>data[MAXN*4];
int n,m;
void Build(int rt,int l,int r)
{
    if(l==r)
    {
        data[rt].push_back(a[l]);
    }
    else
    {
        int mid=(l+r)>>1;
        Build(lch,l,mid);
        Build(rch,mid+1,r);
        data[rt].resize(r-l+1);
        merge(data[lch].begin(),data[lch].end(),data[rch].begin(),data[rch].end(),data[rt].begin());
    }
    //for(int i=0;i<int(data[rt].size());i++)
    //  printf("%d ",data[rt][i]);
    //printf(":%d %d %d\n",rt,l,r);
}
int check(int i,int j,int x,int rt,int l,int r)
{
    if(j<l||r<i)
        return 0;
    if(i<=l&&r<=j)
        return upper_bound(data[rt].begin(),data[rt].end(),x)-data[rt].begin();
    int mid=(l+r)>>1;
    return check(i,j,x,lch,l,mid)+check(i,j,x,rch,mid+1,r);
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        nums[i]=a[i];
    }
    sort(nums+1,nums+n+1);
    Build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int l,r,k;
        scanf("%d %d %d",&l,&r,&k);
        int lb=0,ub=n+1;
        while(ub-lb>1)
        {
            int mid=(lb+ub)>>1;
            if(check(l,r,nums[mid],1,1,n)>=k)
                ub=mid;
            else
                lb=mid;
        }
        printf("%d\n",nums[ub]);
    }
}

注:merge函数的作用是:将两个有序的序列合并为一个有序的序列。函数参数:merge(first1,last1,first2,last2,result,compare);如果要合并的是vector要事先用resize。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值