数据结构-划分树

划分树:解决静态区间第K值问题

#include<bits/stdc++.h>
using namespace std;
const int N=200005,M=20;
int n,m;
#define mid ((le+ri)>>1)
#define lson le,mid,dep+1
#define rson mid+1,ri,dep+1
struct Node{
	int num[N],toleft[N];
};
Node t[M];
int sorted[N];
void build(int le,int ri,int dep){
	if(le==ri){ //如果达到叶节点,返回
		return;
	}
	int key=sorted[mid]; //找到目前这个区间的中位数
	int equ=mid-le+1; //中位数左边应该有多少个数
	for(int i=le;i<=ri;i++){
		if(t[dep].num[i]<key){
			equ--; 
			//左边应该都是<=中位数的数,所以这样能找到需要往左边填几个等于中位数的数
		}
	}
	int tl=0;//指已经进入左子区间多少个数了
	int it1=le-1,it2=mid; //左右儿子指针
	for(int i=le;i<=ri;i++){ //枚举区间内每个数
		int now=t[dep].num[i];
		if(now<key||(now==key&&equ)){ //如果这个数小于中位数||这个数等于中位数且左边区间没有被填满
			if(now==key){
				equ--; //等于的数填进去后,需要等于中位数的数填的数量--
			}
			tl++; //左边区间的数+1
			t[dep+1].num[++it1]=now; //记得是下一层,下一层就是这个区间的左右区间
		}
		else{
			t[dep+1].num[++it2]=now; //同上
		}   
		t[dep].toleft[i]=tl;  //记录这个数所在的区间内的数有多少去了他左儿子内及左儿子左边
	}
	build(lson);//向下递归
	build(rson);
}
int query(int le,int ri,int dep,int x,int y,int z){
	//区间左坐标,区间右坐标,深度,查询的区间左坐标,查询的区间右坐标,查询区间的第几大值
	if(le==ri){  //如果到达叶节点,返回这个点的值
		return t[dep].num[le];
	}
	int tl=0,del=t[dep].toleft[y];//这个区间的子区间内的数+左边的数
	if(le!=x){
		tl=t[dep].toleft[x-1];//减去之前的区间的数
		del-=tl;
	}   
	//此时del代表这个区间的子左区间的数
	int nx,ny;  //新区间左右坐标
	if(del>=z){ 
		//如果子左区间的数多于你要找的值那么就在左区间里
		nx=le+tl;
		ny=nx+del-1;
		return query(lson,nx,ny,z);
	}
	else{
		//否则在右区间里   
		nx=mid+1+x-tl-le;
		ny=nx+y-x-del;
		return query(rson,nx,ny,z-del);
	}   
}

int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
		int x;
		scanf("%d",&x);
		t[0].num[i]=x;
		sorted[i]=t[0].num[i];
	}
	sort(sorted+1,sorted+1+n);
	build(1,n,0);
	for(int i=1;i<=m;i++){
		int x,y,z;
		scanf("%d %d %d",&x,&y,&z);
		int ans=query(1,n,0,x,y,z);
		printf("%d\n",ans);
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值