主席树

题目
题意:询问任意区间第k小数

#include<bits/stdc++.h>
using namespace std ;
const int N = 100010 ;
int n , m , sz , len , a[N] , b[N] , p;
int rt[N<<5] ;//记录第i棵权值线段树的根节点编号
int lc[N<<5] ; //记录每个结点的左子树编号
int rc[N<<5] ;
int sum[N<<5];//记录区间有多少个点值

void build(int &rt , int l , int r){
    rt = sz++;
    if(l == r) return ;
    int mid = l + r >> 1 ;
    build(lc[sz] , l , mid);
    build(rc[sz] , mid + 1 , r);
}

int update(int rt , int l , int r){//依据前一棵树建造第当前这棵树,类似于前缀和
    int ne = sz++;
    lc[ne] = lc[rt] , rc[ne] = rc[rt] , sum[ne] = sum[rt] + 1 ;
    if(l == r) return ne ;
    int mid = l + r >> 1 ;
    if(mid >= p){
        lc[ne] = update(lc[rt] , l , mid);
    }else{
        rc[ne] = update(rc[rt] , mid + 1 , r);
    }
    return ne ;
}
int query(int u , int v , int l , int r , int k){
    if(l == r) return l;
    int mid = l + r >> 1 , x = sum[lc[v]] - sum[lc[u]] ;
    if(x >= k){
        return query(lc[u] , lc[v] , l , mid , k);
    }else{
        return query(rc[u] , rc[v] , mid + 1 , r , k-x);
    }
}

int main(){
    int n , m ;
    scanf("%d%d" , &n , &m);
    for(int i = 1 ; i <= n ; i++){
        scanf("%d" , &a[i]);
        b[i] = a[i];
    }
    sort(b + 1 , b + 1 + n);
    len = unique(b + 1 , b + 1 + n) - b - 1 ;
    build(rt[0] , 1 , len);
    for(int i = 1 ; i <= n ; i++){
        p = lower_bound(b + 1 , b + 1 + len , a[i]) - b ;
        rt[i] = update(rt[i-1] , 1 , len);
    }
    while(m--){
        int l , r , k ;
        scanf("%d%d%d" , &l , &r , &k);
        printf("%d
" , b[query(rt[l-1] , rt[r] , 1 , len , k)]);
    }
}

原文地址:https://www.cnblogs.com/nonames/p/13801477.html