洛谷 P3834:【模板】可持久化线段树 2(静态区间第 k 小)← 主席树

​【题目来源】
https://www.luogu.com.cn/problem/P3834

【题目描述】
这是个非常经典的可持久化权值线段树入门题——静态区间第 k 小。
如题,给定 n 个整数构成的序列 a,将对于指定的闭区间 [l,r] 查询其区间内的第 k 小值。

【输入格式】
第一行包含两个整数,分别表示序列的长度 n 和查询的个数 m。
第二行包含 n 个整数,第 i 个整数表示序列的第 i 个元素 ai。
接下来 m 行每行包含三个整数 l,r,k , 表示查询区间 [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

【数据范围】
对于 10% 的数据,满足 1≤n,m≤10。
对于 25% 的数据,满足 1≤n,m≤10^3。
对于 40% 的数据,满足 1≤n,m≤10^5。
对于 50% 的数据,满足 1≤n,m≤2×10^5,0≤ai≤2×10^5。
对于所有数据,满足 1≤n,m≤2×10^5,0≤ai≤10^9,1≤l≤r≤n,1≤k≤r−l+1。

【算法分析】
● 在信息学竞赛中,用于求解静态区间第 k 小数的可持久化权值线段树,俗称主席树。所谓可持久化,指保存历史版本。所谓权值线段树,是将数值的值域作为划分依据,树上每个节点对应一段值域区间,维护该值域区间内元素的总出现次数。
● 集合关系:主席树 ⊂ 可持久化线段树

【算法代码】

#include <bits/stdc++.h>
using namespace std;

const int N=2e5+5;
int root[N],a[N],tot;
vector<int> v;

struct Node {
    int le,ri,cnt;
} tr[N*40];

int getid(int x) {
    return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}

int build(int le,int ri) {
    int p=++tot;
    if(le==ri) return p;
    int mid=(le+ri)/2;
    tr[p].le=build(le,mid);
    tr[p].ri=build(mid+1,ri);
    return p;
}

int update(int pre,int le,int ri,int x) {
    int p=++tot;
    tr[p]=tr[pre];
    if(le==ri) {
        tr[p].cnt++;
        return p;
    }
    int mid=(le+ri)/2;
    if(x<=mid) tr[p].le=update(tr[pre].le,le,mid,x);
    else tr[p].ri=update(tr[pre].ri,mid+1,ri,x);
    tr[p].cnt=tr[tr[p].le].cnt+tr[tr[p].ri].cnt;
    return p;
}

int query(int u,int v,int le,int ri,int k) {
    if(le==ri) return le;
    int mid=(le+ri)/2;
    int cnt=tr[tr[v].le].cnt-tr[tr[u].le].cnt;
    if(k<=cnt) return query(tr[u].le,tr[v].le,le,mid,k);
    else return query(tr[u].ri,tr[v].ri,mid+1,ri,k-cnt);
}

int main() {
    int n,m;
    cin>>n>>m;
    for(int i=1; i<=n; i++) {
        cin>>a[i];
        v.push_back(a[i]);
    }
    sort(v.begin(),v.end());
    v.erase(unique(v.begin(),v.end()),v.end());

    int len=v.size();
    root[0]=build(1,len);
    for(int i=1; i<=n; i++) {
        root[i]=update(root[i-1],1,len,getid(a[i]));
    }

    while(m--) {
        int le,ri,k;
        cin>>le>>ri>>k;
        int id=query(root[le-1],root[ri],1,len,k);
        cout<<v[id-1]<<endl;
    }

    return 0;
}

/*
in:
5 5
25957 6405 15770 26287 26465
2 2 1
3 4 1
4 5 1
1 2 2
4 4 1

out:
6405
15770
26287
25957
26287
*/



【参考文献】
https://blog.csdn.net/hnjzsyjyj/article/details/120247436


 

​

posted @ 2026-10-27 21:55  Triwa  阅读(3)  评论(0)    收藏  举报