HDU2665 Kth number(主席树入门)

传送门:http://acm.hdu.edu.cn/showproblem.php?pid=2665
主席树呢,就是可持久化线段树,非常神奇。我在B站看了qsc大神的视频之后就大概懂意思了,先做个入门题,再慢慢学。
打个广告:http://www.bilibili.com/video/av4619406/
题意是求区间内第k大的数(实际上是k小),主席树是这样做的,把n个数离散化之后建一个线段树,每个叶子节点表示第x大的数分别有几个,然后pushup上来这个区间内的数字总数。在这个线段树中如果要求第k大的数很容易,就是找k在左区间还是右区间,在左区间的话,就是继续找k。在右区间的话,就是k-左区间总数。找到叶子节点之后,这个下标就是第k个数的离散化后的下标。那么我们对每个前缀建一个线段树,就是通过主席树来实现。然后在[l,r]区间求k的话,第r个线段树-第l-1个线段树,就是这个区间内的线段树,然后用上面的思路来找第k个数就可以了。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
#define pb push_back
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define calm (l+r)>>1
const int INF=1e9+7;

const int maxn=100000;
int n,m,tot;
int a[maxn+10],rt[maxn+10];
struct node{
    int l,r,sum;
}tree[maxn*20];
vector<int> v;
inline int getid(int x){
    return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
void build(int l,int r,int &x){
    x=++tot;
    tree[x].sum=0;
    if(l==r)return;
    int m=(l+r)>>1;
    build(l,m,tree[x].l);
    build(m+1,r,tree[x].r);
}
void update(int l,int r,int &x,int y,int k){
    x=++tot;tree[x]=tree[y];tree[x].sum++;
    if(l==r){
        return;
    }
    int m=(l+r)>>1;
    if(k<=m)update(l,m,tree[x].l,tree[y].l,k);
    else update(m+1,r,tree[x].r,tree[y].r,k);
}
int query(int l,int r,int x,int y,int k){
    if(l==r)return l;
    int m=(l+r)>>1;
    int sum=tree[tree[y].l].sum-tree[tree[x].l].sum;
    if(k<=sum)return query(l,m,tree[x].l,tree[y].l,k);
    else return query(m+1,r,tree[x].r,tree[y].r,k-sum);
}
int main(){
    //freopen("D://input.txt","r",stdin);
    int T;scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        v.clear();
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            v.pb(a[i]);
        }
        sort(v.begin(),v.end());
        v.erase(unique(v.begin(),v.end()),v.end());
        int cnt=v.size();
        tot=0;build(1,cnt,rt[0]);
        for(int i=1;i<=n;i++){
            update(1,cnt,rt[i],rt[i-1],getid(a[i]));
        }
        while(m--){
            int l,r,x;scanf("%d%d%d",&l,&r,&x);
            printf("%d\n",v[query(1,cnt,rt[l-1],rt[r],x)-1]);
        }
    }
    return 0;
}

你可能感兴趣的:(线段树)