luogu3834模板可持久化线段树 2(主席树),静态区间第K小值

Posted 小哈里

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu3834模板可持久化线段树 2(主席树),静态区间第K小值相关的知识,希望对你有一定的参考价值。

problem

solution

题目:有n个数,多次询问一个区间[L,R]中第k小的值是多少。
思路:

  • 查询[1,n]中的第k小值:
    • 先对数据进行离散化,然后按值域建立线段树,线段树中维护某个值域中的元素个数, 在线段树的 每个结点上记录这一个值域中的元素个数
    • 那么要寻找第k小值,从根结点开始处理,若左儿子中表示的元素个数大于等于k,那么我们递归的处理左儿子,寻找左儿子中第k小的数,若左儿子中的元素个数小于k,那么第k小的数在右儿子中,我们寻找右儿子中第k-(左儿子中的元素数)小的数。
  • 查询区间[L,R]中的第K小值:
    • 我们按照从1到n的顺序依次将数据插入可持久化的线段树中,将会得到n+1个版本的值域线段树(包括初始化的版本),将其编号为0~n。可以发现所有版本的线段树都拥有相同的结构,它们同一个位置上的结点的含义都相同。
    • 考虑第i个版本的线段树的结点P,P中储存的值 表示[1,i]这个区间中,P结点的值域中所含的元素个数 ;假设我们知道了[1,R]区间中P结点的值域中所含的元素个数,也知道[1,L-1]区间中P结点的值域中所包含的元素个数,显然用第一个个数减去第二个个数,就可以得到[L,R]区间中的元素个数(即该区间的值域线段树)。
    • 因此我们对于一个查询[L,R],同步考虑两个根root[L-1]与root[R],用它们同一个位置的结点的差值就表示了区间[L,R]中的元素个数,利用这个性质,从两个根节点,向左右儿子中递归的查找第k小数即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;

int a[maxn], b[maxn];

int rt[maxn], lch[maxn<<5], rch[maxn<<5], sum[maxn<<5], tot;//主席树
void build(int &p, int l, int r){
	p = ++tot;
	if(l==r)return ;
	int mid = (l+r)>>1;
	build(lch[p], l, mid);
	build(rch[p], mid+1, r);
}
int update(int p, int l, int r, int x){	//b[x]+=1;
	int np = ++tot;
	lch[np]=lch[p], rch[np] = rch[p], sum[np] = sum[p]+1;
	if(l==r)return np;
	int mid = (l+r)>>1;
	if(x<=mid)lch[np] = update(lch[np], l, mid, x);
	else rch[np] = update(rch[np], mid+1, r, x);
	return np;
}
int query(int u, int v, int l, int r, int k){
	int ans, mid=(l+r>>1), x=sum[lch[v]]-sum[lch[u]];
	if(l==r)return l;
	if(x>=k)ans = query(lch[u], lch[v], l, mid, k);
	else ans = query(rch[u], rch[v], mid+1, r, k-x);
	return ans;
}

int main(){
	int n, q;  cin>>n>>q;
	for(int i = 1; i <= n; i++)cin>>a[i], b[i]=a[i];
	sort(b+1,b+n+1);
	int m = unique(b+1,b+n+1)-b-1;
	build(rt[0], 1, m);						   //用离散化后的数据建立初始的值域线段树,区间加维护每个元素(位置)的出现次数
	for(int i = 1; i <= n; i++){
		int p = lower_bound(b+1,b+m+1, a[i])-b;//查询a[i]离散化后对应的值
		rt[i] = update(rt[i-1], 1, m, p);	   //建立第i个版本的值域线段树,维护[1,i]时,每个区间的元素个数
	}
	while(q--){
		int l, r, k;  cin>>l>>r>>k;
		int ans = query(rt[l-1],rt[r],1,m,k); //查询用第r个版本和第l-1个版本之间值域差值查
		cout<<b[ans]<<"\\n";					  //返回离散化对应的值
	}
	return 0;
}

以上是关于luogu3834模板可持久化线段树 2(主席树),静态区间第K小值的主要内容,如果未能解决你的问题,请参考以下文章

luogu3834模板可持久化线段树 2(主席树),静态区间第K小值

Luogu P3834 模板可持久化线段树 1(主席树)

P3834 模板可持久化线段树 1(主席树) 整体二分

luogu P3834 模板可持久化线段树 1(主席树)| 静态第k小问题^&

解题报告:P3834 模板可持久化线段树 2(主席树)详解

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