K-th Number POJ - 2104

K-th Number

 POJ - 2104 

You are working for Macrohard company in data structures department. After failing your previous task about key insertion you were asked to write a new data structure that would be able to return quickly k-th order statistics in the array segment. 
That is, given an array a[1...n] of different integer numbers, your program must answer a series of questions Q(i, j, k) in the form: "What would be the k-th number in a[i...j] segment, if this segment was sorted?" 
For example, consider the array a = (1, 5, 2, 6, 3, 7, 4). Let the question be Q(2, 5, 3). The segment a[2...5] is (5, 2, 6, 3). If we sort this segment, we get (2, 3, 5, 6), the third number is 5, and therefore the answer to the question is 5.

Input

The first line of the input file contains n --- the size of the array, and m --- the number of questions to answer (1 <= n <= 100 000, 1 <= m <= 5 000). 
The second line contains n different integer numbers not exceeding 10 9 by their absolute values --- the array for which the answers should be given. 
The following m lines contain question descriptions, each description consists of three numbers: i, j, and k (1 <= i <= j <= n, 1 <= k <= j - i + 1) and represents the question Q(i, j, k).

Output

For each question output the answer to it --- the k-th number in sorted a[i...j] segment.

Sample Input

7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3

Sample Output

5
6
3

Hint

This problem has huge input,so please use c-style input(scanf,printf),or you may got time limit exceed.

题目大意:

给定一个序列有N个数字

对应的给定M个查询

查询给定区间[i,j] ,要求查询第k个大的数字

思路:

利用分桶法,其分成若干个桶区间

因为要求解的是第k大的数字

所以我们要做的是,在将原先的序列从小到大排序好放在num[]数组里,然后

再用二分搜索的方法,搜索x

只要x 满足在这个区间里查找到的不大于这个x的个数正好为k,那么这个就是所求的数字。

代码:

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<vector>
using namespace std;

const int MAXN = 1e5+5;
const int MAXM = 5005;
int A[MAXN];
const int B=1000;
int num[MAXN];

vector<int> bucker[MAXN/B];
int main(){
	int N,M;
	scanf("%d %d",&N,&M);
	for(int i=0;i<N;i++){
		scanf("%d",&A[i]);
		num[i] = A[i];
		bucker[i/B].push_back(A[i]);
	}
	for(int i=0;i<N/B;i++){
		sort(bucker[i].begin(),bucker[i].end());
	}
	sort(num,num+N);
	int l;
	int r;
	int c;
	for(int i=0;i<M;i++){
		
		scanf("%d %d %d",&l,&r,&c);
		l = l-1;
		r = r;
		
		int lb = -1,ub = N-1;
		
		while(ub-lb>1){
			int mid = (lb+ub)/2;
			int num_c = num[mid];
			
			int k = 0;
			int tl = l;
			int tr = r;
			while(tl<tr && tl%B!=0) if(A[tl++]<=num_c) k++;
			while(tl<tr && tr%B!=0) if(A[--tr]<=num_c) k++;
			
			while(tl<tr){
				int j = tl/B;
				k += upper_bound(bucker[j].begin(),bucker[j].end(),num_c)-bucker[j].begin();
				tl+=B;
			}
			
			if(k>=c) ub = mid;
			else lb = mid;		
		}
		printf("%d\n",num[ub]);	
	}
		
	return 0;
} 

线段树代码:

思路,对应的利用线段树进行维护,每个节点维护对应区间的序列,并且这个序列有序。

同时应注意merge的用法。

当查询区间没有交集时,则直接返回0

当查询区间完全包含时,则直接进行二分查找,返回不小于x 的数的个数

当有部分交集时,则递归进行相应的查询即可。

注意:对应的区间线段树区间是左开右闭

二分查找也是左开右闭

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<vector>
using namespace std;

const int MAXN = 1e5+5;
const int MAXM = 5005;
int A[MAXN];
const int B=1000;
int num[MAXN];

vector<int> dat[MAXN<<2];


// [l,r)
void init(int k,int l, int r){
	if(r-l==1){
		dat[k].push_back(A[l]);
	}else{
		int chl = k*2+1;
		int chr = k*2+2;
		
		init(chl,l,(l+r)/2);
		init(chr,(l+r)/2,r);
		dat[k].resize(r-l);
		merge(dat[chl].begin(),dat[chl].end(),dat[chr].begin(),dat[chr].end(),dat[k].begin());
	}
}

// [i,j)
int query(int i,int j,int x,int k,int l, int r){
	
	if(j<=l || i>=r) return 0;
	else if(i<=l && j>=r) {
		return upper_bound(dat[k].begin(),dat[k].end(),x) - dat[k].begin();	
	}else{
	
		int ls =query(i,j,x,2*k+1,l,(l+r)/2); 
		int rs =query(i,j,x,2*k+2,(l+r)/2,r);
		return ls+rs;
	}
	return 0;
}


int main(){
	int N,M;
	scanf("%d %d",&N,&M);
	for(int i=0;i<N;i++){
		scanf("%d",&A[i]);
		num[i] = A[i];
	}
	
	sort(num,num+N);
	init(0,0,N);
	int l;
	int r;
	int c;
	for(int i=0;i<M;i++){
		
		scanf("%d %d %d",&l,&r,&c);
		l = l-1;
		r = r;
		
		int lb = -1,ub = N-1;
		
		while(ub-lb>1){
			int mid = (lb+ub)/2;
			int num_c = num[mid];
			
			int k = query(l,r,num_c,0,0,N);
			
			if(k>=c) ub = mid;
			else lb = mid;		
		}
		printf("%d\n",num[ub]);	
	}
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/Willen_/article/details/86711531