数据结构--主席树(不带修改)

主席树原理就是区间内的第k大可以通过每个数字的出现次数随便乱加减搞出来,然后,就可以像前缀和那样建n个线段树,就可以查询区间第k大了!
主要问题就是空间绝对爆炸,大概n2logn左右。
看这个图:(来自blog
这里写图片描述
可以发现每一颗线段树结构都相同,只是每次都有logn个节点的值不一样,其他节点完全可以“借”来用。这样空间理论上就降到了nlogn级别的,实现也很容易。
代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=200001;
int n,m,size,b[maxn],v[maxn],tot,root[maxn];
struct Node{
    int l,r,size;
}a[maxn*30];
void build(int l,int r,int &node){
    node=++tot;
    if(l==r)return;
    int mid=l+r>>1;
    build(l,mid,a[node].l);
    build(mid+1,r,a[node].r);
}
void addtree(int l,int r,int &node,int pre,int x){
    node=++tot;
    a[node]=a[pre];
    a[node].size++;
    if(l==r)return;
    int mid=l+r>>1;
    if(x<=mid)addtree(l,mid,a[node].l,a[pre].l,x);
    else addtree(mid+1,r,a[node].r,a[pre].r,x);
}
int query(int l,int r,int lx,int rx,int k){
    if(l==r)return l;
    int lsize=a[a[rx].l].size-a[a[lx].l].size;
    int mid=l+r>>1;
    if(k<=lsize)return query(l,mid,a[lx].l,a[rx].l,k);
    else return query(mid+1,r,a[lx].r,a[rx].r,k-lsize);
}
inline int hash1(int k){
    return lower_bound(b+1,b+size+1,k)-b;
}
int main(){
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&v[i]);
        b[i]=v[i];
    }
    sort(b+1,b+n+1);
    size=unique(b+1,b+n+1)-b-1;
    build(1,size,root[0]);
    for(int i=1;i<=n;i++){
        addtree(1,size,root[i],root[i-1],hash1(v[i]));
    }
    for(int i=1;i<=m;i++){
        int l,r,k;
        scanf("%d %d %d",&l,&r,&k);
        int x=query(1,size,root[l-1],root[r],k);
        printf("%d
",b[x]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/stone41123/p/7581272.html