可持久化线段树(主席树)
title: 可持久化线段树(主席树)
categories:
- ICPC
tags:
- null
abbrlink: d1a237fb
date: 2023-04-02 00:00:00
给定 n 个整数构成的序列 a,将对于指定的闭区间 [l,r] 查询其区间内的第 k 小值。
- 题目一开始的离散化复杂度为$O(n\log n)$,构建基础主席树复杂度为$O(n\log n)$,统计并插入的复杂度是$O(n\log n + n\log n)=O(n\log n)$,询问的复杂度是$O(m\log n)$。复杂度总和就是$O((m+n)\log n)$。
#define N 200005
#define lc(x) tr[x].l
#define rc(x) tr[x].r
struct node{
int l,r,s; //s:节点值域中有多少个数
}tr[N*20];
int root[N],idx;
int n,m,a[N];
vector<int> b;
void build(int &x,int l,int r){
x=++idx;
if(l==r) return;
int m=l+r>>1;
build(lc(x),l,m);
build(rc(x),m+1,r);
}
void insert(int x,int &y,int l,int r,int k){
y=++idx; tr[y]=tr[x]; tr[y].s++;
if(l==r) return;
int m=l+r>>1;
if(k<=m) insert(lc(x),lc(y),l,m,k);
else insert(rc(x),rc(y),m+1,r,k);
}
int query(int x,int y,int l,int r,int k){
if(l==r) return l;
int m=l+r>>1;
int s=tr[lc(y)].s-tr[lc(x)].s;
if(k<=s) return query(lc(x),lc(y),l,m,k);
else return query(rc(x),rc(y),m+1,r,k-s);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++){
scanf("%d",&a[i]); b.push_back(a[i]);
}
sort(b.begin(),b.end());
b.erase(unique(b.begin(),b.end()),b.end());
int bn=b.size();
build(root[0],1,bn);
for(int i=1; i<=n; i++){
int id=lower_bound(b.begin(),b.end(),a[i])-b.begin()+1;
insert(root[i-1],root[i],1,bn,id);
}
while(m--){
int l,r,k; scanf("%d%d%d",&l,&r,&k);
int id=query(root[l-1],root[r],1,bn,k)-1;
printf("%d\n",b[id]);
}
return 0;
}
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 爱飞鱼的blog!