洛谷P3834 【模板】可持久化线段树1 主席树(珂持久化线段树)

题目链接:传送门
思路:构造一棵权值线段树,让其珂持久化(即一棵主席树)。
主席树变量:

int n,m,a[Size];		//题目给出的输入数据
int maxn,b[Size];		//离散化后的数据,maxn表示去重后的数的个数
int tot;				//当前主席树内共有多少个节点
int T[Size];			//T[i]表示第i个历史版本的根节点
int ls[Size],rs[Size];	//ls[i],rs[i]分别表示主席树上i节点的左右儿子
int sum[Size];			//表示主席树上i节点的子树中共有多少个树

建树:

int Build(int l,int r) {	//建出[l,r]区间
	int rt=++tot;			//主席树内的节点数++
	sum[rt]=0;				//一开始树内没有数
	if(l<r) {
		int mid=(l+r)>>1;
		ls[rt]=Build(l,mid);
		rs[rt]=Build(mid+1,r);
	}
	return rt;
}

更新:

int Update(int pre,int l,int r,int v) {
	//在前一个历史版本的基础上把v加进去
	int rt=++tot;
	//rt的左右孩子先指向pre的左右孩子
	ls[rt]=ls[pre]; rs[rt]=rs[pre];
	//rt的子树内数的数量比pre多1
	sum[rt]=sum[pre]+1;
	if(l<r) {
		int mid=(l+r)>>1;
		if(v<=mid) {
			ls[rt]=Update(ls[pre],l,mid,v);
		} else {
			rs[rt]=Update(rs[pre],mid+1,r,v);
		}
	}
	return rt;
}

询问第k小:

int Query(int u,int v,int l,int r,int k) {
	//[u,v]表示询问的区间
	//[l,r]表示当前区间
	if(l==r)	return l;
	int mid=(l+r)>>1,x=sum[ls[v]]-sum[ls[u]];
	//每次在主席树上二分,x表示左子树中有多少个数
	if(x>=k)	return Query(ls[u],ls[v],l,mid,k);
	return Query(rs[u],rs[v],mid+1,r,k-x);
}

完整代码:

#include
#include
#include
#define re register int
#define rl register ll
using namespace std;
typedef long long ll;
int read() {
	re x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9') {
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9') {
		x=10*x+ch-'0';
		ch=getchar();
	}
	return x*f;
}
const int Size=200005;
const int LOG=20;
int n,m,tot,maxn,a[Size],b[Size],T[Size];
int ls[Size*LOG],rs[Size*LOG],sum[Size*LOG];
int Build(int l,int r) {
	int rt=++tot;
	sum[rt]=0;
	if(l<r) {
		int mid=(l+r)>>1;
		ls[rt]=Build(l,mid);
		rs[rt]=Build(mid+1,r);
	}
	return rt;
}
int Update(int pre,int l,int r,int v) {
	int rt=++tot;
	ls[rt]=ls[pre]; rs[rt]=rs[pre];
	sum[rt]=sum[pre]+1;
	if(l<r) {
		int mid=(l+r)>>1;
		if(v<=mid) {
			ls[rt]=Update(ls[pre],l,mid,v);
		} else {
			rs[rt]=Update(rs[pre],mid+1,r,v);
		}
	}
	return rt;
}
int Query(int u,int v,int l,int r,int k) {
	if(l==r)	return l;
	int mid=(l+r)>>1,x=sum[ls[v]]-sum[ls[u]];
	if(x>=k)	return Query(ls[u],ls[v],l,mid,k);
	return Query(rs[u],rs[v],mid+1,r,k-x);
}
int main() {
	n=read();
	m=read();
	for(re i=1; i<=n; i++)	b[i]=a[i]=read();
	sort(b+1,b+1+n);
	maxn=unique(b+1,b+1+n)-(b+1);
	T[0]=Build(1,maxn);
	for(re i=1; i<=n; i++) {
		int t=lower_bound(b+1,b+1+maxn,a[i])-b;
		T[i]=Update(T[i-1],1,maxn,t);
	}
	while(m--) {
		int l=read();
		int r=read();
		int k=read();
		int id=Query(T[l-1],T[r],1,maxn,k);
		printf("%d\n",b[id]);
	}
	return 0;
}

你可能感兴趣的:(C++模板系列,Luogu题目,洛谷,模板,可持久化,线段树,主席树)