可持久化线段树(主席树)模板

题目背景

这是个非常经典的主席树入门题——静态区间第K小

数据已经过加强,请使用主席树。同时请注意常数优化

题目描述

如题,给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值。

输入输出格式

输入格式:
第一行包含两个正整数N、M,分别表示序列的长度和查询的个数。

第二行包含N个正整数,表示这个序列各项的数字。

接下来M行每行包含三个整数 l, r, kl,r,k , 表示查询区间 [l, r][l,r] 内的第k小值。

输出格式:
输出包含k行,每行1个正整数,依次表示每一次查询的结果

输入输出样例

输入样例#1: 复制
5 5
25957 6405 15770 26287 26465
2 2 1
3 4 1
4 5 1
1 2 2
4 4 1
输出样例#1: 复制
6405
15770
26287
25957
26287

n,m<=2*10^5

代码

#include<bits/stdc++.h>

using namespace std;
const int MAXN = 200010<<5;

int n,m,a[MAXN],rt[MAXN],sum[MAXN],b[MAXN];
int cnt,l[MAXN],r[MAXN];

inline int build(int L,int R){
    int rt=++cnt;
    if(L<R){
        int mid=(L+R)>>1;
        l[rt]=build(L,mid);
        r[rt]=build(mid+1,R);
    }
    return rt;
}   

inline int update(int pre,int L,int R,int c){
    int rt=++cnt;
    l[rt]=l[pre],r[rt]=r[pre],sum[rt]=sum[pre]+1;
    if(L<R){
        int mid=(L+R)>>1;
        if(c<=mid) l[rt]=update(l[pre],L,mid,c);
        else r[rt]=update(r[pre],mid+1,R,c);
    }
    return rt;
}

inline int query(int u,int v,int L,int R,int k){
    if(L==R) return L;
    int x=sum[l[v]]-sum[l[u]];
    int mid=(L+R)>>1;
    if(x>=k) return query(l[u],l[v],L,mid,k);
    else return query(r[u],r[v],mid+1,R,k-x);
}

int main(){
    scanf("%d%d",&n,&m);
    for(register int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        b[i]=a[i];
    }
    sort(b+1,b+1+n);
    int u=unique(b+1,b+1+n)-b-1;
    rt[0]=build(1,u);
    for(register int i=1;i<=n;i++){
        a[i]=lower_bound(b+1,b+1+u,a[i])-b;
        rt[i]=update(rt[i-1],1,u,a[i]);
    }
    while(m--){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        printf("%d\n",b[query(rt[x-1],rt[y],1,u,z)]);
    }   
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40448823/article/details/80657143