主席树板子(可持久化线段树)

            主席树写的比较好的博客: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;
} 

  

猜你喜欢

转载自blog.csdn.net/qq_42198700/article/details/88877125