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

来源:互联网 发布:阿里云多备案服务号 编辑:程序博客网 时间:2024/05/22 04:29

本题是利用了归并排序的过程,其实归并树就是线段树+归并,只是线段树每个区间里存了这个区间里的有序序列,注意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;}


原创粉丝点击