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

D e s c r i p t i o n Description Description

静态区间第 k k k

n ≤ 1 0 5 n\leq 10^5 n105


S o l u t i o n Solution Solution

主席树模板题

首先对原数组离散化后,依次对放入每个点建一棵权值线段树

这样的话我们就可以利用前缀和的思想,在 T [ y ] T[y] T[y] T [ x − 1 ] T[x-1] T[x1]的差构成的权值线段树中查询第 k k k大即为答案


C o d e Code Code

#include
#include 
#include
#define N 200010
using namespace std;
int T[N],sum[N<<5],L[N<<5],R[N<<5],a[N],b[N],n,m,cnt,q,x,y,z;
inline long long read()
{
    char c;int f=0,d=1;
    while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
    while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
    return d*f;
}
inline int build(int l,int r)//建树
{
	int rt=++cnt,mid=l+r>>1;
	if(l==r) {L[rt]=R[rt]=rt;return rt;}
	L[rt]=build(l,mid);
	R[rt]=build(mid+1,r);
	return rt;
}
inline int updata(int pre,int l,int r,int x)//在pre这个数的基础上对l,r这个区间插入x
{
	int rt=++cnt,mid=l+r>>1;
	L[rt]=L[pre];R[rt]=R[pre];sum[rt]=sum[pre]+1;
	if(l<r)
	{
		if(x<=mid) L[rt]=updata(L[pre],l,mid,x);
		else R[rt]=updata(R[pre],mid+1,r,x);
	}
	return rt;
}
inline int query(int p,int q,int l,int r,int k)//查询在q和p这两个树每个节点的值分别作差后的树上查询第k大
{
	if(l>=r) return l;
	int x=sum[L[q]]-sum[L[p]],mid=l+r>>1;//取出左边范围内的数的个数
	if(x>=k) return query(L[p],L[q],l,mid,k);//小于等于k说明第k小一定在这之间
	else return query(R[p],R[q],mid+1,r,k-x);//否则在它的右子树
}
signed main()
{
	n=read();q=read();
	for(register 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;
	T[0]=build(1,m);
	for(register int i=1;i<=n;i++)
	{
		int t=lower_bound(b+1,b+1+m,a[i])-b;//离散化
		T[i]=updata(T[i-1],1,m,t);
	}
	while(q--)
	{
		x=read();y=read();z=read();
		int t=query(T[x-1],T[y],1,m,z);
		printf("%d\n",b[t]);
	}
}

你可能感兴趣的:(主席树,权值线段树,线段树)