【线段树+归并排序】poj 2104

本题是利用了归并排序的过程,其实归并树就是线段树+归并,只是线段树每个区间里存了这个区间里的有序序列,注意rank的求法,二分答案,求出每个中间值mid在原始序列区间[s,t]里的rank,其中这里面又可以用二分来求mid在[s,t]里排第几,最后相加就是这个rank,比较蛋疼的就是二分时的位置问题,什么时候+1,什么时候-1,很难搞!

每次询问的复杂度O(lgn*lgn*lgn),所以总共为O(m*lgn*lgn*lgn)

ps:归并树原理:

 如果对于一段区间,仅查找一次第k大元素的话好说,直接一个快排搞定。
    如果有多次离线询问,然后就可以通过归并排序,建一棵归并树(nlogn)对于树的每一个节点,通过归并排序递归的建立一个序列,其中每个节点[l,r]表示原序列中,[l,r]这些数字排序以后的状态。如图,红色节点表示会被分到左子树。


就这样,在区间[l,r]查找第k大元素的时候,先二分枚举元素x,求出x为第几大元素,再在归并树里查找相应区间,对于每一个被包含的区间,二分查找有多少个比当前枚举的元素x小,有多少元素小于等于当前枚举元素x,如果k刚好在这两段区间里,x就是第k大数。程序巨猥琐于是我没写- -!总体复杂度n(logn)^3……强烈膜拜想出此算法的。

这题竟然可以sort水过~震惊了!

#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <string.h>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <limits.h>

using namespace std;

int lowbit(int t){return t&(-t);}
int countbit(int t){return (t==0)?0:(1+countbit(t&(t-1)));}
int gcd(int a,int b){return (b==0)?a:gcd(b,a%b);}
#define LL long long
#define PI acos(-1.0)
#define N  100001
#define MAX INT_MAX
#define MIN INT_MIN
#define eps 1e-8
#define FRE freopen("a.txt","r",stdin)

int n,m;
int mertree[30][N];
int a[N];

struct node
{
    int l,r;
};
node tree[N*3];

void Build(int i,int s,int t,int dep){//自下而上建归并树
    tree[i].l=s;
    tree[i].r=t;
    if(s==t){
        mertree[dep][s]=a[s];
        return ;
    }
    int mid=(s+t)>>1;
    Build(2*i,s,mid,dep+1);
    Build(2*i+1,mid+1,t,dep+1);

    int l=s,r=mid+1;
    int cnt=s;
    while(l<=mid && r<=t){
        if(mertree[dep+1][l]<mertree[dep+1][r])
            mertree[dep][cnt++]=mertree[dep+1][l++];
        else
            mertree[dep][cnt++]=mertree[dep+1][r++];
    }
    if(l==mid+1)
    while(r<=t)
        mertree[dep][cnt++]=mertree[dep+1][r++];
    else
    while(l<=mid)
        mertree[dep][cnt++]=mertree[dep+1][l++];
}

int solve(int i,int s,int t,int val,int dep){           //O(lgn)
    if(s<=tree[i].l && t>=tree[i].r){
        int l=tree[i].l,r=tree[i].r;
        int pos=lower_bound(&mertree[dep][l], &mertree[dep][r]+1, val)-&mertree[dep][l];//s,t子区间是已经排好序的,这是也要二分计算当前val在这个子区间里的rank,最后相加就是val在s,t区间里的rank------------O(lgn)
        return pos;
    }
    int res=0;
    if(s<=tree[2*i].r)
    res+=solve(2*i,s,t,val,dep+1);
    if(t>=tree[2*i+1].l)
    res+=solve(2*i+1,s,t,val,dep+1);
    return res;
}

int main(){
    while(scanf("%d%d",&n,&m)!=EOF){
        int i,j;
        for(i=1;i<=n;i++)scanf("%d",&a[i]);
        Build(1,1,n,1);
        while(m--){
            int s,t,rank;
            scanf("%d%d%d",&s,&t,&rank);
            rank--;// !!!!!
            int l=1,r=n,mid;
            while(l<r){         //O(lgn)
                mid=(l+r+1)>>1;
                int tmp=solve(1,s,t,mertree[1][mid],1);//mertree[1]是已经排好序的序列,用二分找出当前要查询的rank
                if(tmp<=rank)l=mid;
                else
                r=mid-1;
            }
            printf("%d\n",mertree[1][l]);
        }
    }
    return 0;
}


sort~~:

#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <string.h>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <limits.h>

using namespace std;

int lowbit(int t){return t&(-t);}
int countbit(int t){return (t==0)?0:(1+countbit(t&(t-1)));}
int gcd(int a,int b){return (b==0)?a:gcd(b,a%b);}
#define LL long long
#define PI acos(-1.0)
#define N  100001
#define MAX INT_MAX
#define MIN INT_MIN
#define eps 1e-8
#define FRE freopen("a.txt","r",stdin)

struct node
{
    int pos;
    int val;
}a[N];
bool cmp(node x,node y){
    return x.val<y.val;
}
int main(){
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF){
        int i,j,k;
        for(i=1;i<=n;i++)
        {
            scanf("%d",&a[i].val);
            a[i].pos=i;
        }
        sort(a+1,a+n+1,cmp);
        while(m--){
            int s,t,rank;
            scanf("%d%d%d",&s,&t,&rank);
            int cnt=0;
            for(i=1;i<=n;i++){
                if(s<=a[i].pos && a[i].pos<=t)
                    cnt++;
                if(cnt==rank)break;
            }
            printf("%d\n",a[i].val);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值