1. 程式人生 > >hdu 2665 Kth number(函式化線段樹)

hdu 2665 Kth number(函式化線段樹)

題意:給定一個序列,問區間內第K大的數是多少。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
#define maxn 100005
#define lson l,mid
#define rson mid+1,r
map<int,int>ms;
int node=0;
int head[maxn],a[maxn],b[maxn],sum[maxn<<5],L[maxn<<5],R[maxn<<5];
//函式化線段樹就是在保留之前節點的基礎上新建節點,並且利用之前的節點進行壓縮空間。
//此處是n*logn的空間複雜度。利用了區間數值數目的可加性。
//[1,l] 的線段樹儲存的是權值在到al為止(1,num)範圍內數值的出現次數。
//兩個線段樹相減就是在區間範圍內某些數字的出現次數。進而統計第K大的數字。

void init()
{
    //memset(L,0,sizeof(L));
   // memset(R,0,sizeof(R));
    memset(head,0,sizeof(head));
    node=0;ms.clear();
}
void build(int &o,int l,int r)//dfs序建立基礎線段樹
{
    o=++node;sum[o]=0;
    if(l>=r)return;
    int mid=(l+r)>>1;
    build(L[o],lson);
    build(R[o],rson);
}
void update(int pre,int &o,int l,int r,int x)//pre表示同樣位置的上一棵樹節點
{
    o=++node;
    L[o]=L[pre];R[o]=R[pre];sum[o]=sum[pre]+1;
    int mid=(l+r)>>1;
    if(l>=r)return;
    if(x<=mid)
        update(L[pre],L[o],lson,x);
    else
        update(R[pre],R[o],rson,x);
}
int query(int l,int r,int a,int b,int k)
{
    if(l==r)return l;
    int mid=(l+r)>>1;
    int x=sum[L[b]]-sum[L[a]];
    if(x>=k)
        return query(lson,L[a],L[b],k);
    else
        return query(rson,R[a],R[b],k-x);
}
int main()
{
    int t,l,r,k;
    scanf("%d",&t);
    while(t--){
        int n,m;
        init();
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            b[i]=a[i];
        }
        sort(b+1,b+n+1);
        int num=unique(b+1,b+n+1)-b-1;
        for(int i=1;i<=num;i++)ms[b[i]]=i;
            build(head[0],1,n);
        for(int i=1;i<=n;i++){
            update(head[i-1],head[i],1,num,ms[a[i]]);
        }
        while(m--){
            scanf("%d%d%d",&l,&r,&k);
            printf("%d\n",b[query(1,num,head[l-1],head[r],k)]);
        }
    }
    return 0;
}