poj 2104:K-th Number(划分树,经典题)

K-th Number
Time Limit: 20000MS   Memory Limit: 65536K
Total Submissions: 35653   Accepted: 11382
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.

Source

Northeastern Europe 2004, Northern Subregion
 
  划分树,经典题
  关于划分树的解释可以参考博文:sdut 2610:Boring Counting(第四届山东省省赛原题,划分树 + 二分)。这个属于高级点的应用,至于基础的介绍,我就不多写了(懒人一个。。),可以参考这篇博客,介绍的很详细:划分树
  这里简单一提,划分树的主要作用是用来求“区间内第k大的元素”,这道题属于经典应用,就是赤裸裸的求一个区间内第k大的数。题目“K-th Number”已经标明了这种含义。
  因为之前做过一道划分树的题,所以无耻的套用了上道题的代码,然后就改了改就无耻的过了。O_o,原谅我吧。
  代码
 1 #include <iostream>

 2 #include <stdio.h>

 3 #include <algorithm>

 4 using namespace std;  5 #define MAXN 100005

 6 struct Divide_tree{ //划分树的结构(4个数组,2个操作)

 7     int arr[MAXN];  //原数组

 8     int sorted[MAXN];   //排序后数组

 9     int sum[20][MAXN];  //记录第i层1~j划分到左子树的元素个数(包括j)

10     int dat[20][MAXN];  //记录第i层元素序列

11     void build(int c,int L,int R)   //建树,主要是建立sum[][]和dat[][]数组

12  { 13         int mid = (L+R)>>1; 14         int lsame = mid-L+1;    //lsame用来记录和中间值val_mid相等的,且可以分到左孩子的数的个数 15                                 //简单来说就是可以放入左孩子的,与中间值val_mid相等的数的个数

16         int lp=L,rp=mid+1;  //当前节点的左孩子和右孩子存数的起点

17         for(int i=L;i<mid;i++)  //获得一开始的lsame

18             if(sorted[i]<sorted[mid]) 19                 lsame--; 20         for(int i=L;i<=R;i++){  //从前往后遍历一遍, 21                                 //确定当前节点区间内的所有元素的归属(放在左孩子或者放在右孩子)

22             if(i==L) sum[c][i]=0; 23             else sum[c][i]=sum[c][i-1]; 24             if(dat[c][i]<sorted[mid]){  //当前元素比中间值val_mid小,放入左孩子

25                 dat[c+1][lp++] = dat[c][i]; 26                 sum[c][i]++; 27  } 28             else if(dat[c][i]>sorted[mid])  //当前元素比中间值val_mid大,放入右孩子

29                 dat[c+1][rp++] = dat[c][i]; 30             else{  //当前元素值与中间值val_mid相等,根据lsame数判断放入左孩子还是右孩子

31                 if(lsame){ 32                     lsame--; 33                     sum[c][i]++; 34                     dat[c+1][lp++]=sorted[mid]; 35  } 36                 else{ 37                     dat[c+1][rp++]=sorted[mid]; 38  } 39  } 40  } 41         if(L==R) return ;   //递归出口,遇到叶子节点

42         build(c+1,L,mid);   //递归进入左孩子区间

43         build(c+1,mid+1,R); //递归进入右孩子区间

44  } 45     int query(int c,int L,int R,int ql,int qr,int k) 46  { 47         //c为树的层数,L,R为当前节点的区间范围,ql,qr为查询的区间范围,k为查询范围内第k大的数

48         if(L==R)    //递归出口,返回第k大的数

49             return dat[c][L]; 50         int s;  //记录[L,ql-1]中进入左孩子的元素的个数

51         int ss; //记录[ql,qr]中进入左孩子的元素的个数

52         int mid=(L+R)>>1; 53         if(L==ql){  //端点重合的情况,单独考虑

54             s=0; 55             ss=sum[c][qr]; 56  } 57         else { 58             s=sum[c][ql-1]; 59             ss=sum[c][qr]-s; 60  } 61         if(k<=ss)   //左孩子的元素个数大于k个,说明第k大的元素一定在左孩子区间中,到左孩子中查询

62             return query(c+1,L,mid,L+s,L+s+ss-1,k); 63         else

64             return query(c+1,mid+1,R,mid+1+ql-s-L,mid+1+qr-s-ss-L,k-ss); 65  } 66 }; 67 Divide_tree tree;   //定义划分树

68 int main() 69 { 70     int i,L,R,N,M,k; 71     scanf("%d%d",&N,&M); 72     for(i=1;i<=N;i++){   //输入

73         scanf("%d",&tree.arr[i]); 74         tree.sorted[i]=tree.dat[0][i]=tree.arr[i]; 75  } 76     sort(tree.sorted+1,tree.sorted+N+1); 77     tree.build(0,1,N); 78     for(i=1;i<=M;i++){  //M次询问

79         scanf("%d%d%d",&L,&R,&k); 80         printf("%d\n",tree.query(0,1,N,L,R,k)); 81  } 82 

83     return 0; 84 }

 

  Freecode : www.cnblogs.com/yym2013

你可能感兴趣的:(number)