主席树板子(递增序列)

第k大的数

描述
你为Macrohard公司的数据结构部门工作,你的工作是重新写一个数据结构,这个数据结构能快速地找到一段数列中第k大的数。

就是说,给定一个整数数列a[1…n],其中每个元素都不相同,你的程序要能回答一组格式为Q (i , j , k)的查询,Q(i, j ,k)的意思是“在a[i…j]中第k大的数是多少?”

例如令 a = {1, 5, 2, 6, 3, 7, 4},查询格式为Q (2 , 5 , 3),数列段a[2…5] = {5, 2, 6, 3},第3大的数是5,所以答案是5。

输入
文件第一行包括一个正整数n,代表数列的总长度,还有一个数m,代表有m个查询。 n,m满足:1≤n≤100 000, 1≤m≤5 000 第二行有n个数,代表数列的元素,所有数都不相同,而且不会超过109 接下来有m行,每行三个整数i , j , k,代表一次查询, i , j , k满足1≤i≤j≤n, 1≤k≤j − i + 1

输出
输出每个查询的答案,用换行符隔开

样例输入 [复制]
7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3
样例输出 [复制]
5
6
3

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int data=0;int w=1; char ch=0;
    ch=getchar();
    while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
    return data*w;
}
const int N=1e5+10;
int L[N<<5],R[N<<5],T[N],sum[N<<5];
int n,m,tot=0;
int a[N],w[N];
inline int  build(int l,int r){
	int rt=++tot;sum[rt]=0;
	if(l<r){
		int mid=(l+r)>>1;
		L[rt]=build(l,mid);
		R[rt]=build(mid+1,r);
	}
	return rt;
}
inline int  update(int pre,int l,int r,int x){
	int rt=++tot;
	L[rt]=L[pre],R[rt]=R[pre],sum[rt]=sum[pre]+1;
	if(l<r){
		int mid=(l+r)>>1;
		if(x<=mid){
		    L[rt]=update(L[pre],l,mid,x);
		}
		else{
			R[rt]=update(R[pre],mid+1,r,x);
		}
	}
	return rt;
}
inline int que(int prel,int prer,int l,int r,int k){
	if(l>=r) return l;
	int mid=(l+r)>>1;
	int num=sum[L[prer]]-sum[L[prel]];
	if(num>=k) return que(L[prel],L[prer],l,mid,k);
	else return que(R[prel],R[prer],mid+1,r,k-num);
}
int main(){
    n=read();m=read();
    for(int i=1;i<=n;i++){
    	a[i]=read();w[i]=a[i];
    }
	sort(w+1,w+n+1);
	int s1=unique(w+1,w+n+1)-w-1;
	T[0]=build(1,s1);
	for(int i=1;i<=n;i++){
		int x=lower_bound(w+1,w+s1+1,a[i])-w;
	   T[i]=update(T[i-1],1,s1,x);	 
	}
	while(m--){
		int l=read(),r=read(),k=read();
		int x=que(T[l-1],T[r],1,s1,k);
		printf("%d\n",w[x]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值