可持久化线段树

可持久化权值线段树(主席树)

一个常见模板题静态区间第k大

一个容易出现的思路:先对数组在值域上进行离散化,那么建树过程可以变成每次在离散的值域区间上单点权值+1。“可持久化”用logn的时间和空间来维护不同版本。
每次找区间第k大=>两个不同的历史版本找最小的r,使得两版本在[1,r]的前缀和之差恰好为k。

code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>
inline void read(T &x){
	x=0;T fl=1;char tmp=getchar();
	while(tmp<'0'||tmp>'9')fl=tmp=='-'?-fl:fl,tmp=getchar();
	while(tmp>='0'&&tmp<='9')x=(x<<3)+(x<<1)+tmp-'0',tmp=getchar();
	x=x*fl;
}
const ll mod=1e9+7;
const int maxn=2e5+100;
int a[maxn],b[maxn];
int n,m,q;
struct node{
	int l,r,val;
}t[maxn<<5];
int root[maxn];int clk;
int build(int l,int r){
	int rt=++clk;
	t[clk].val=0;
	if(r>l){
		int mid=l+r>>1;
		t[rt].l=build(l,mid);
		t[rt].r=build(mid+1,r);
	}
	return rt;
}
int insert(int u,int l,int r,int pos){
	int rt=++clk;
	t[rt].val=t[u].val+1;
	if(r>l){
		int mid=l+r>>1;
		if(pos<=mid){
			t[rt].r=t[u].r;
			t[rt].l=insert(t[u].l,l,mid,pos);
		}
		else{
			t[rt].l=t[u].l;
			t[rt].r=insert(t[u].r,mid+1,r,pos);
		}
	}
	return rt;
}
int query(int u,int v,int l,int r,int cnt){
	if(l>=r) return l;
	int mid=l+r>>1;
	if(t[t[v].l].val-t[t[u].l].val>=cnt){
		return query(t[u].l,t[v].l,l,mid,cnt);
	}
	else 
		return query(t[u].r,t[v].r,mid+1,r,cnt-(t[t[v].l].val-t[t[u].l].val));
}
signed main(){
	cin>>n>>q;
	for(int i=1;i<=n;i++)
		read(a[i]),b[i]=a[i];
	sort(b+1,b+n+1);
	m=unique(b+1,b+n+1)-b-1;
	root[0]=build(1,m);
	for(int i=1;i<=n;i++){
		int key=lower_bound(b+1,b+m+1,a[i])-b;
		root[i]=insert(root[i-1],1,m,key);
	}
	while(q--){
		int l,r,k;
		read(l),read(r),read(k);
		printf("%d\n",b[query(root[l-1],root[r],1,m,k)]);
	}
	return 0;
}

posted @ 2022-09-05 18:03  xyc1719  阅读(14)  评论(0编辑  收藏  举报