洛谷 P3834 【模板】可持久化线段树 2(主席树)

洛谷 P3834 【模板】可持久化线段树 2(主席树)

题目链接

题目背景

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

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

题目描述

如题,给定 n 个整数构成的序列 a,将对于指定的闭区间 [l, r] 查询其区间内的第 k 小值。

输入格式

第一行包含两个整数,分别表示序列的长度 n 和查询的个数 m。
第二行包含 n 个整数,第 i 个整数表示序列的第 ii 个元素 a i a_i ai
接下来 m 行每行包含三个整数 l , r , k , l, r, k, l,r,k, 表示查询区间 [ l , r ] [l, r] [l,r] 内的第 k 小值。

输出格式

对于每次询问,输出一行一个整数表示答案。

样例

输入

5 5
25957 6405 15770 26287 26465 
2 2 1
3 4 1
4 5 1
1 2 2
4 4 1

输出

6405
15770
26287
25957
26287

主席树模板题,AC代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2e5+5;

int n,m,siz,l,r,k,cnt=0;
int a[N];//原数组
int s[N];//去重后的数组
int rk[N];//记录原数组的元素的排名
int root[N];//记录每个根节点的编号

struct ptree{
    
    
    int l,r,sum;//sum记录经过该节点的次数
}t[N*20];

void build(int &node,int l,int r){
    
    
    node=++cnt;
    if(l==r) return;
    int mid=l+r>>1;
    build(t[node].l,l,mid);
    build(t[node].r,mid+1,r);
}

void update(int &node,int last,int l,int r,int s){
    
    
    node=++cnt;
    t[node]=t[last];
    ++t[node].sum;
    if(l==r) return;
    int mid=l+r>>1;
    if(s<=mid) update(t[node].l,t[last].l,l,mid,s);
    else update(t[node].r,t[last].r,mid+1,r,s);
}

int query(int node,int last,int l,int r,int k) {
    
    
    if (l == r) return s[l];
    int sum = t[t[node].l].sum - t[t[last].l].sum, mid = l + r >> 1;
    if (k <= sum) return query(t[node].l, t[last].l, l, mid, k);
    else return query(t[node].r, t[last].r, mid + 1, r, k - sum);
}

int main(){
    
    
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    memcpy(s,a,sizeof(s));
    sort(s+1,s+1+n);
    siz=unique(s+1,s+1+n)-s-1;
    build(root[0],1,siz);
    for(int i=1;i<=n;i++) rk[i]=lower_bound(s+1,s+1+siz,a[i])-s;
    for(int i=1;i<=n;i++) update(root[i],root[i-1],1,siz,rk[i]);
    while(m--){
    
    
        scanf("%d%d%d",&l,&r,&k);
        printf("%d\n",query(root[r],root[l-1],1,siz,k));
    }
}

猜你喜欢

转载自blog.csdn.net/qq_43765333/article/details/108529702