洛谷 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 小数的可持久化权值线段树++ ,俗称主席树。所谓++可持久化,指保存历史版本++ 。所谓权值线段树,是将数值的值域作为划分依据,树上每个节点对应一段值域区间,维护该值域区间内元素的总出现次数。
● 集合关系:主席树 ⊂ 可持久化线段树

【算法代码一】

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

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

struct Node {
    int L,R,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].L=build(le,mid);
    tr[p].R=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].L=update(tr[pre].L,le,mid,x);
    else tr[p].R=update(tr[pre].R,mid+1,ri,x);
    tr[p].cnt=tr[tr[p].L].cnt+tr[tr[p].R].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].L].cnt-tr[tr[u].L].cnt;
    if(k<=cnt) return query(tr[u].L,tr[v].L,le,mid,k);
    else return query(tr[u].R,tr[v].R,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
*/

【算法代码二】
算法代码二仅将算法代码一中的结构体成员变量 L、R 分别换成了 le、ri,并对代码中其他地方做了相应修改。

cpp 复制代码
#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

相关推荐
hnjzsyjyj4 小时前
洛谷 P3567:[POI 2014] KUR-Couriers ← 主席树
主席树
alexwang2113 个月前
HDU 4348 详细题解
c++·算法·题解·hdu·主席树·可持久化数据结构·可持久化线段树
Ayanami_Reii10 个月前
区间不同数的个数-树状数组/线段树/莫队/主席树
数据结构·c++·算法·线段树·树状数组·主席树·莫队
Ayanami_Reii1 年前
详解Splay平衡树
数据结构·算法·线段树·主席树·持久化线段树
Ayanami_Reii1 年前
进阶数据结构应用-SPOJ 3267 D-query
数据结构·算法·线段树·主席树·持久化线段树
Assassin's Creed2 年前
2024江苏省赛E. Divide
c语言·数据结构·c++·算法·acm·主席树
rgw20102 年前
P10789 [NOI2024] 登山
数据结构·动态规划·树上倍增·主席树·树链剖分·前缀优化
邪神与厨二病3 年前
HUAWEI Programming Contest 2024(AtCoder Beginner Contest 342)(A,B,C,D,E,F,G)
数据结构·算法·华为·线段树·动态规划·dijkstra·主席树