POJ2104-K-th Number-求区间第K大数(暴力or归并树or划分树)

题目链接:http://poj.org/problem?id=2104

题目意思很简单,就是给你一个序列,查询某区间第K大的数;

方法1:时间复杂度O(N*M);不支持更新操作,代码简单;

利用结构体排序,保留原数据的顺序。

#include <stdio.h>
#include <iostream>
#include <algorithm>
#define N 100000
using namespace std;
/*
    这个思路很好;时间复杂度O(n*m);
    不过还好这个题目给的数据量不大,而且时间限制给了2s,所以可以很轻松的过;
*/
struct NODE
{
    int x,id;
}node[N];
bool cmp(NODE a,NODE b)
{
    return a.x<b.x;
}
int main()
{
    int i,j,n,m,a,b,k;
    while(~scanf("%d%d",&n,&m))
    {
        for(i=0;i<n;i++)
        {
            scanf("%d",&node[i].x);
            node[i].id=i;
        }
        sort(node,node+n,cmp);
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d",&a,&b,&k);
            a--,b--;
            for(j=0;j<n;j++)
            {
                if(node[j].id>=a&&node[j].id<=b)    k--;
                if(!k)
                {
                    printf("%d\n",node[j].x);
                    break;
                }
            }
        }
    }
    return 0;
}

 

方法2:归并树,时间复杂度O(nlogn+mlong^3(n));利用归并排序,用线段树位数数组顺序;

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<queue>
#include<map>
#include<cmath>
#include<stack>
#include<set>
#include<vector>
#include<algorithm>
#define LL long long
#define inf 1<<30
#define s(a) scanf("%d",&a)
#define Clear(a,b) memset(a,b,sizeof(a))
using namespace std;
/*
    O(nlogn+mlog^3(n))
*/
const int N=200005;
int n,m,a,b;
int seg[25][N];
int num[N];
void Merge_Sort(int l,int r,int deep)   //  每一层保留该层已经排好的数据;
{
    int mid=(l+r)>>1;
    int i=l,j=mid+1,k=l;
    while(i<=mid&&j<=r){
        if(seg[deep+1][i]<=seg[deep+1][j]){
            seg[deep][k++]=seg[deep+1][i++];
        }else{
            seg[deep][k++]=seg[deep+1][j++];
        }
    }
    while(j<=r){
        seg[deep][k++]=seg[deep+1][j++];
    }
    while(i<=mid){
        seg[deep][k++]=seg[deep+1][i++];
    }
}
void Build(int l,int r,int rt,int deep)    //   建树;deep根节点的深度;
{
    if(l==r){
        seg[deep][l]=num[l];        //  单叶子节点按输入的顺序存数据;
        return;
    }
    int mid=(l+r)>>1;
    Build(l,mid,rt<<1,deep+1);
    Build(mid+1,r,rt<<1|1,deep+1);
    Merge_Sort(l,r,deep);       //  归并排序;
}
int Query(int v,int deep,int L,int R,int l,int r,int rt)    //  查询[L,R]区间比v小的数有多少个;
{
    if(r<L||R<l) return 0;
    if(L<=l&&R>=r){
        return lower_bound(&seg[deep][l],&seg[deep][r]+1,v)-&seg[deep][l];  //  返回第一个大于等于v的下标;
    }
    int mid=(l+r)>>1;
    return Query(v,deep+1,L,R,l,mid,rt<<1)+Query(v,deep+1,L,R,mid+1,r,rt<<1|1);
}
int Solve(int l,int r,int k){       //  二分查找[L,R],降低时间复杂度;
    int L=1,R=n,mid;
    while(L<R){
        mid=(L+R+1)>>1;     //  这里要特别注意;mid=(L+R+1)>>1;不是mid=(L+R)>>1;
        int cnt=Query(seg[1][mid],1,l,r,1,n,1);     //  返回比v小的个数;
        if(cnt<=k){     //  如果小于等于我们要查找的k,说明seg[1][mid]这个数太小了,网右边查找;
            L=mid;
        }else{
            R=mid-1;
        }
    }
    return seg[1][L];
}
int main()
{
    while(~scanf("%d%d",&n,&m)){
        for(int i=1;i<=n;i++) s(num[i]);
        Build(1,n,1,1);
        while(m--){
            int l,r,k;
            s(l);s(r);s(k);
            printf("%d\n",Solve(l,r,k-1));  //  这里也值得注意一下,输入的是k-1,不是k;
        }
    }
    return 0;
}

方法3:划分树;好吧,这个思路我还没有整理好,给这个牛逼算法留一块空地;^_^....

你可能感兴趣的:(number,poj2104,划分树,归并树,K-th,求区间第k大数)