[主席树]hdu2665 区间第k大

Input
The first line is the number of the test cases.
For each test case, the first line contain two integer n and m (n, m <= 100000), indicates the number of integers in the sequence and the number of the quaere.
The second line contains n integers, describe the sequence.
Each of following m lines contains three integers s, t, k.
[s, t] indicates the interval and k indicates the kth big number in interval [s, t]


Output
For each test case, output m lines. Each line contains the kth big number.


Sample Input
1
10 1
1 4 2 3 5 6 7 8 9 0
1 3 2


Sample Output

2

【题意】

t组样例,有n个数,m个询问。

m个询问中有l,r,k;

询问在[l,r]区间中,第k大的数是多少。

【解法】

将n个数离散化(假设离散化后下标为1-t)后建立一颗区间为1-t的线段树,每个[x,x]都表示离散化后下标为x的数的个数。

用主席数记录每加入一个数后该线段树的变化。

最后求[l,r]区间的第k大时

两颗差值树中  左子树的size表示比当前根小的数的个数,如果左子树size>=k就在左子树中找,否则就去右子树中找k-左子树size的值。。。。。

呃,感觉解释不清。。意会意会吧。。上代码了。。

 

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#define N 100005
using namespace std;
int tree[N*25],lchild[N*25],rchild[N*25],root[N];
int a[N],b[N],cnt;
map<int,int>mp;
void build(int l,int r,int root){
	if(l==r){
		tree[root]=0;
		return;
	}
	int mid=l+r>>1;
	build(l,mid,lchild[root]=++cnt);
	build(mid+1,r,rchild[root]=++cnt);
	tree[root]=tree[lchild[root]]+tree[rchild[root]];
	return ;
}
void update(int last,int cur,int l,int r,int x){
	tree[cur]=tree[last];
	lchild[cur]=lchild[last];
	rchild[cur]=rchild[last];
	if(l==r){
		tree[cur]++;
		return;
	}
	int mid=l+r>>1;
	if(x<=mid)	update(lchild[last],lchild[cur]=++cnt,l,mid,x);
	else update(rchild[last],rchild[cur]=++cnt,mid+1,r,x);
	tree[cur]=tree[lchild[cur]]+tree[rchild[cur]];
	return ;
}
int query(int last,int cur,int l,int r,int k){
	if(l==r){
		//printf("l=%d r=%d k=%d\n",l,r,k);
		return l;
	}
	int mid=l+r>>1;
	int now=tree[lchild[cur]]-tree[lchild[last]];//注意判断的是左子树的size哦 
//	printf("left=%d mid=%d right=%d now=%d k=%d\n",l,mid,r,now,k);
	if(now>=k)	return query(lchild[last],lchild[cur],l,mid,k);
	else return query(rchild[last],rchild[cur],mid+1,r,k-now);
}
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		cnt=0;
		mp.clear();//??? 
		int n,m,ss=0;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			b[i]=a[i];
		}
		sort(b+1,b+n+1);
	/*	for(int i=1;i<=n;i++){
			if(i!=n)	printf("b[%d]=%d ",i,b[i]);
			else printf("b[%d]=%d\n",i,b[i]);
		}
		*/
		int t=unique(b+1,b+1+n)-b-1;//离散化 
		//printf("t=%d\n",t);
		build(1,t,++cnt);
		root[0]=1; 
		for(int i=1;i<=t;i++){
			mp[b[i]]=i;
		}
		for(int i=1;i<=n;i++){
			root[i]=++cnt;
			update(root[i-1],root[i],1,t,mp[a[i]]);
		}
		while(m--){
			int left,right,k;
			scanf("%d%d%d",&left,&right,&k);
			printf("%d\n",b[query(root[left-1],root[right],1,t,k)]);//query返回的是第k大的数离散化后的下标,所以b[query]才是原来的值 
		}
	}
	return 0;
} 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值