主席树写的比较好的博客:https://www.cnblogs.com/LonecharmRiver/articles/9087536.html
题目描述:静态区间L,R的最小值。
方法:离散化输入数组,以离散化之后的数组建立多颗线段树。因为数据为离散化之后的满足单调,直接查询就可。
问题一:何为离散化????
(其实我也不知道) 把静态区间排序,去重之后剩余的即为离散化之后的数组。此操作建议使用C++中STL。
sort(b+1,b+n+1);
q=unique(b+1,b+n+1)-b-1;//强大的STl
问题二:开多颗线段树不会MLE????
这就利用了可持久化的原理,因为每棵线段树和前一颗线段树有共同的空间,直接连边即可。
一: 我们,以第零个节点为初始,构建一个长度为离散化B数组长度的空线段树
inline void build(int &k,int l,int r){//初始化建树
k=node_cnt++;//全局变量统计节点
if(l==r) return;
int mid=(l+r)/2;
build(lc[k],l,mid);//左子树
build(rc[k],mid+1,r);//右子树
}
二:进行点修改
for(register int i=1;i<=n;i++){
p=lower_bound(b+1,b+q+1,a[i])-b;//本质二分查找,寻找第i个数在b中的位置
rt[i]=modify(rt[i-1],1,q);//进行点修改操作
}
(点修改函数)modify 函数
inline int modify(int k,int l,int r){
int oo=++node_cnt;
lc[oo]=lc[k];rc[oo]=rc[k];sum[oo]=sum[k]+1;//新建节点及统计子节点个数
if(l==r) return oo;//寻找到数底直接返回即可
int mid=(l+r)/2;
if(p<=mid) lc[oo]=modify(lc[oo],l,mid);//若P点在左子树,修改左子节点
else rc[oo]=modify(rc[oo],mid+1,r);//相反
return oo;
}
因为主席树可以进行加减操作,在查询L,R区间时,利用前缀和的思想,R修改以后的线段树,和L-1修改以后的线段树之间的排名为K。
inline int query(int u,int v,int l,int r,int k){
int mid=(l+r)/2;
int cnt=sum[lc[v]]-sum[lc[u]],ans;//左子树子节点的个数
if(l==r) return l;//返回b数组中的位置
if(cnt>=k) ans=query(lc[u],lc[v],l,mid,k);//节点数比左子树节点多
else ans=query(rc[u],rc[v],mid+1,r,k-cnt);//相反
return ans;
}
全部代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=2*(1e5+7);
int n,m,rt[N<<5],lc[N<<5],rc[N<<5],a[N],b[N],sum[N<<5];
int q,node_cnt=0,p;
inline void build(int &k,int l,int r){
k=node_cnt++;
if(l==r) return;
int mid=(l+r)/2;
build(lc[k],l,mid);
build(rc[k],mid+1,r);
}
inline int modify(int k,int l,int r){
int oo=++node_cnt;
lc[oo]=lc[k];rc[oo]=rc[k];sum[oo]=sum[k]+1;
if(l==r) return oo;
int mid=(l+r)/2;
if(p<=mid) lc[oo]=modify(lc[oo],l,mid);
else rc[oo]=modify(rc[oo],mid+1,r);
return oo;
}
inline int query(int u,int v,int l,int r,int k){
int mid=(l+r)/2;
int cnt=sum[lc[v]]-sum[lc[u]],ans;
if(l==r) return l;
if(cnt>=k) ans=query(lc[u],lc[v],l,mid,k);
else ans=query(rc[u],rc[v],mid+1,r,k-cnt);
return ans;
}
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+n+1);
q=unique(b+1,b+n+1)-b-1;
build(rt[0],1,q);
for(register int i=1;i<=n;i++){
p=lower_bound(b+1,b+q+1,a[i])-b;
rt[i]=modify(rt[i-1],1,q);
}
for(register int i=1;i<=m;i++){
int k,l,r;
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",b[query(rt[l-1],rt[r],1,q,k)]);
}
return 0;
}