[模板] 主席树

define 想不清楚回到定义

可持久化权值线段树

个人理解:

  • 与线段树不同的地方在于,线段树维护的是某一段序列的某个特征。

  • 带权线段树维护的是区间的桶,以数据的值作为下标,像桶一样记录数据出现的次数,且带有线段树的特征。


主席树

  • 主席树是一种特殊的带权二叉树,或者说他由 n 个带权二叉树组成,可以在很快的时间内求出任意子序列的第 k 小值。

  • 主席树采用动态开点存储。

原理

假如我有 \(1-n\)\(n\) 个数,每有一个数,我就形成一棵带权二叉树(可以先这么理解),那么要查询区间 \([ l , r ]\) 上的第 k 小值,就让第 r 棵带权二叉树与第 l-1 棵相减,设某个对应结点为[ x ,y ],那么差值就表示 [ l , r ]上有这么多个数在[ x , y ]之间。是不是很简单?

其他操作类似于线段树,不在赘述。

Updated on 2021-06-14

今天重新捡起了主席树,发现当时码风极其独特 markdown 也极其独特

重新更新一下代码,包含了自己动态开点线段树的习惯,好记。

总代码

//Shiyan Wang
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
template <typename T>
inline T read(){
	char ch=getchar();bool fl=false;T x=0;
	while(!isdigit(ch)){if(ch=='-')fl=true;ch=getchar();}
	while(isdigit(ch)){
		x=(x<<3)+(x<<1)+(ch^48);ch=getchar();
	}
	return fl?-x:x;
}
const int maxn = 2e5 + 10;
#define mid ((l+r)>>1)
int n,q,m,cnt=0;
int a[maxn],b[maxn],rt[maxn];
int sz[maxn<<5],ls[maxn<<5],rs[maxn<<5];
void build(int &p,int l,int r){
	p=++cnt;
	if(l==r)return ;
	sz[p]=0;
	build(ls[p],l,mid);build(rs[p],mid+1,r);
}
void update(int &p,int pre,int l,int r,int val){
	p=++cnt;
	ls[p]=ls[pre],rs[p]=rs[pre],sz[p]=sz[pre]+1;
	if(l==r)return ;
	if(val<=mid)update(ls[p],ls[pre],l,mid,val);
	else update(rs[p],rs[pre],mid+1,r,val);
}
int query(int u,int v,int l,int r,int k){
	if(l>=r)return l;
	int x=sz[ls[v]]-sz[ls[u]];
	if(x>=k)return query(ls[u],ls[v],l,mid,k);
	else return query(rs[u],rs[v],mid+1,r,k-x);
}
#define read() read<int>()
int main(){
	n=read();q=read();
	for(int i=1;i<=n;i++)a[i]=read(),b[i]=a[i];
	sort(b+1,b+1+n);
	m=unique(b+1,b+1+n)-(b+1);
	build(rt[0],1,m);//权值线段树
	for(int i=1;i<=n;i++){
		int pos=lower_bound(b+1,b+1+m,a[i])-b;
		update(rt[i],rt[i-1],1,m,pos);
	}
	while(q--){
		int x=read(),y=read(),z=read();
		printf("%d\n",b[query(rt[x-1],rt[y],1,m,z)]);
	}
	return 0;
}
posted @ 2021-08-12 16:31  ¶凉笙  阅读(44)  评论(0编辑  收藏  举报