hdu主席树模板(可持久化线段树/求区间最大 小值)zjhu1020

n个数据建n个版本ver,每个版本里有一个线段树 且只有一条分支不同(即可通过继承得到部分)

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,m,tot=0,ver[maxn];
//区间第k大/小的关键是按 值域(即题目给出的n个值)划分!若值是大数据或重复的就离散化
struct node{
	int l,r,val;//val表示有多少个数在值域区间内
}tree[maxn<<5];//nlogn,logn~20,左移4位不够
//每个版本有一颗树,这颗树的每个节点都是一个树
int build(int l,int r){
	int p=++tot;
	tree[p].val=0;//初始为0,change时加
	tree[p].l=p;tree[p].r=p;//ver0是个0树,而该树的每个节点都是一个线段树(的根节点也是0
	return p;//根节点的值给ver
}
void pushup(int p){
	tree[p].val=tree[tree[p].l].val+tree[tree[p].r].val;
}
int change(int rt,int l,int r,int pos){//pos在线段树中从小到大,方便找到第k大的数
	//lr是值域
	int p=++tot;
	node &tt=tree[p];//tt指向新树,tt改变新树改变
	tt=tree[rt];//继承
	if(l==r){
		tt.val++;
		return  p;
	}
	int mid=(l+r)/2;
	if(pos<=mid) tt.l=change(tt.l,l,mid,pos);//新放入一个左树更新了,所以左子树的根改变
	else tt.r=change(tt.r,mid+1,r,pos);
	pushup(p);//改变或构造要pushup,本题构造的特殊可省去
	return p;//根节点的值给ver
}
int ask(int per,int lst,int l,int r,int k){
	if(l==r)return l;//l,r,mid,pos  值域
	node &pp=tree[per],&ll=tree[lst];
	int tx=tree[ll.l].val-tree[pp.l].val;//第几个,与k比较
	//注意这里看两个版本的 左子树  的差,因为第k大要从左往右看
	int mid=(l+r)>>1;//线段树里的位置
	if(tx>=k) return ask(pp.l,ll.l,l,mid,k);
	else return ask(pp.r,ll.r,mid+1,r,k-tx);
}
int main(){
	int x,t,l,r,k;
	cin>>t;
	while(t--) {
		scanf("%d%d",&n,&m);
		tot=0;
		ver[0]=build(1,n);
		for(int i=1;i<=n;i++) {
			scanf("%d",&x);
			ver[i]=change(ver[i-1],1,n,x);
		}
		while(m--){
			scanf("%d%d%d",&l,&r,&k);
			printf("%d\n",ask(ver[l-1],ver[r],1,n,k));
		}	
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值