K-th Number (POJ_2104) 划分树

K-th Number
Time Limit: 20000MS   Memory Limit: 65536K
Total Submissions: 45147   Accepted: 15021
Case Time Limit: 2000MS

Description

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.


题目大意:给出一系列数,求出给定区间的Kth值。


解题思路:划分树。

先建树,然后进行查询。

建树的思路为快速排序,小于中位数的放到左边,大于的放到右边。设当前区间为[l,r],当前为第i个数,用left[i]记录[l,i]内有多少个数划入左边区间。

查询思路为不断缩小区间,直到为单位区间(l==r)。难点在于如何在当前的大区间中确定小区间,这时就要用到,前面记录的划入左边区间的个数。首先要比较当前查询区间(ll,rr)划入左区间的个数num=left[ll-1]-left[rr]和k值大小,如果k<=num,则Kth值在左曲建,否则在右区间。

sort[ ][2  3  3  4  4  5  5  7  9]

tree[0][3  5  7  3  4  9  4  2  5]

tree[1][3  3  4  4  2][5  7  9  5]

tree[2][3  3  2][4  4][5  5][7  9]

tree[3][3  2][3][4][4][5][5][7][9]

tree[4][2][3][3][4][4][5][5][7][9]


当在左区间时,小区间为( l+num , l+left[rr]-1 ),查询第k值

当在右区间时,小区间为( mid+1+( ll-l-left[ll-1] ) , mid+1+rr-l-left[ll-1] ),查询第(k-left[ll-1])值

参考博文:http://blog.csdn.net/dyx404514/article/details/8731696


代码如下:

#include"cstdio"
#include"iostream"
#include"algorithm"
#define MAXN 100000 + 10
using namespace std;
struct seq{
	int order;
	int value;
}a[MAXN];
struct node{	//划分树 
	int val[MAXN];	//存值 
	int left[MAXN];	//划入左子树的个数 
	int deep;		//深度 
}tree[25];
bool cmp(seq a,seq b){
	return a.value<b.value;
} 
//构建划分树 
void Build(int d,int l,int r){
	if(l==r) return ;
	int mid=(l+r)>>1;
	int p=0,t=0;
	for(int i=l;i<=r;i++){
		if(tree[d].val[i]<=mid){//划入左子树 
			tree[d+1].val[l+p]=tree[d].val[i];
			p++;
			tree[d].left[i]=p; 
		}else{//划入右子树 
			tree[d+1].val[mid+1+t]=tree[d].val[i];
			t++;
			tree[d].left[i]=p; //是p不是t 
		}
	}
	Build(d+1,l,mid);
	Build(d+1,mid+1,r);
}
//在大区间(l,r)中查找小区间(ll,rr)的第k大值 	
int Query(int d,int l,int r,int ll,int rr,int k){ 
	if(ll==rr) return (tree[d].val[ll]);
	int ls=0,rs=0;	//ls,rs用于确定小区间
	if(ll>l)
		ls=tree[d].left[ll-1];
		else ls=0;
	int mid=(l+r)>>1;
	rs=tree[d].left[rr];
	if(rs-ls>=k) 
		return Query(d+1,l,mid,l+ls,l+rs-1,k);
	else 
		return Query(d+1,mid+1,r,(mid+1)+(ll-l-ls),(mid+1)+rr-l-rs,k-(rs-ls)); 
	if(ll==rr) return tree[d].val[ll];	
}
int main(){
	int n,m;
	int i;
	while(scanf("%d%d",&n,&m)!=EOF){
		//存值并进行离散化 
		for(i=1;i<=n;i++){
			scanf("%d",&a[i].value);
			a[i].order=i;
		}
		sort(a+1,a+1+n,cmp);
		for(i=1;i<=n;i++){
			tree[0].val[a[i].order]=i;
		}
		Build(0,1,n);
		while(m--){
			int ll,rr,k;
			scanf("%d%d%d",&ll,&rr,&k);
			printf("%d\n",a[Query(0,1,n,ll,rr,k)].value);
		}
	}
	return 0;
}



你可能感兴趣的:(线段树,poj,区间查询,划分树)