划分树。
实际上相当于把数列归并排序的过程用树形结构储存起来,并通过一个辅助二位数组记录数的每一层上的每一个元素被划分到子树的情况(划分到左子树
或右子树)。
归并排序具有稳定性,这也是能够准确查询k-th元素的关键。
通过辅助数组在树形结构中不断判断第k个元素属于哪个子树哪个区间,最终找到此元素。
http://poj.org/problem?id=2104

1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 using namespace std; 6 7 const int maxn = 1e5 + 10; 8 const int maxd = 22; 9 10 int tree[maxd][maxn], left[maxd][maxn]; 11 int s[maxn]; 12 int n, m, l, r, k; 13 14 void build(int d, int l, int r){ 15 if(r - l < 2) return; 16 int mid = (l + r) >> 1; 17 int left_mid = mid - l; 18 for(int i = l; i < r; i++) if(tree[d][i] < s[mid]) left_mid--; 19 int ls = l, rs = mid; 20 for(int i = l; i < r; i++){ 21 int flag = 1; 22 if(tree[d][i] < s[mid]) tree[d + 1][ls++] = tree[d][i]; 23 else if(tree[d][i] == s[mid] && left_mid) tree[d + 1][ls++] = tree[d][i], --left_mid; 24 else tree[d + 1][rs++] = tree[d][i], flag = 0; 25 left[d][i + 1] = left[d][i] + flag; 26 } 27 build(d + 1, l, mid); 28 build(d + 1, mid, r); 29 } 30 31 int query(int k, int l, int r, int d, int L, int R){ 32 //find the k-th number in range[l, r) within tree of depth d 33 if(r - l < 2) return tree[d][l]; 34 int Ll1 = left[d][l] - left[d][L]; 35 //num of elements in range [L, l) divided into left-subtree 36 int Lr1 = left[d][r] - left[d][L]; 37 int Ll2 = l - L - (left[d][l] - left[d][L]); 38 int Lr2 = r - L - (left[d][r] - left[d][L]); 39 int mid = (L + R) >> 1; 40 int cnt_left = Lr1 - Ll1; 41 if(cnt_left >= k) return query(k, L + Ll1, L + Lr1, d + 1, L, mid); 42 else return query(k - cnt_left, mid + Ll2, mid + Lr2, d + 1, mid, R); 43 } 44 45 int main(){ 46 while(~scanf("%d%d", &n, &m)){ 47 for(int i = 0; i < n; i++){ 48 scanf("%d", &s[i]); 49 tree[0][i] = s[i]; 50 } 51 sort(s, s + n); 52 for(int i = 0; i < maxd; i++) left[i][0] = 0; 53 build(0, 0, n); 54 for(int i = 0; i < m; i++){ 55 scanf("%d%d%d", &l, &r, &k); 56 int ans = query(k, l - 1, r, 0, 0, n); 57 printf("%d ", ans); 58 } 59 } 60 return 0; 61 }