数据结构2 静态区间第K大/第K小

给定数组$A[1...N]$, 区间$[L,R]$中第$K$大/小的数的指将$A[L...R]$中的数从大到小/从小到大排序后的第$K$个.

"静态"指的是不带修改.

这个问题有多种做法:

1. 归并排序

POJ 2104, 静态区间第K小

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N(1e5+5);
 5 int a[18][N];
 6 
 7 void merge(int id, int b, int e){
 8     int mid=(b+e)>>1;
 9     for(int l=b, r=mid, i=b; i<e; i++){
10         if(l==mid) a[id][i]=a[id+1][r++];
11         else if(r==e) a[id][i]=a[id+1][l++];
12         else if(a[id+1][l]<=a[id+1][r]) a[id][i]=a[id+1][l++];
13         else a[id][i]=a[id+1][r++]; 
14     }
15 }
16 
17 void build(int id, int b, int e){
18     if(b+1==e){
19         scanf("%d", a[id]+b);
20         return;
21     }
22     int mid=(b+e)>>1;
23     build(id+1, b, mid);
24     build(id+1, mid, e);
25     merge(id, b, e);
26 }
27 //返回k在[l, r)与[L, R)交集上的Rank
28 int Rank(int id, int L, int R, int l, int r, int k){
29     if(l<=L&&R<=r){
30         return lower_bound(a[id]+L, a[id]+R, k)-a[id]-L;
31     }
32     int mid=(L+R)>>1;
33     if(r<=mid){
34         return Rank(id+1, L, mid, l, r, k);
35     }
36     if(l>=mid){
37         return Rank(id+1, mid, R, l, r, k);
38     }
39     return Rank(id+1, L, mid, l, r, k)+Rank(id+1, mid, R, l, r, k);
40 }
41 //返回(l, r]中Rank(x)>=k的最小的x
42 int BS(int b, int e, int k, int n){
43     int l=-1e9-1, r=1e9+1, mid;
44     while(r-l>1){
45         mid=(l+r)>>1;
46         if(Rank(0, 0, n, b, e, mid)>=k) r=mid;
47         else l=mid;
48     }
49     return r;
50 }
51 int main(){
52     int n, m; scanf("%d%d", &n, &m);
53     build(0, 0, n);
54     for(int l, r, k; m--;){
55         scanf("%d%d%d", &l, &r, &k), l--;
56         printf("%d\n", BS(l, r, k, n)-1);
57     }
58 }

 这种做法的想法是将归并排序的过程记录下来, 这样就形成了一棵线段树, 这棵线段树的每个节点记录着它所代表的那个区间[L,R]排好序后的情形.

我们把这样的线段树称做归并排序树, 归并排序树能在$O(\log^2{N})$的复杂度内完成如下查询:

$\text{RANK}(l, r, x)$: 区间$[l,r]$内小于$x$的数的数目.

定义$\text{LEAST}(l, r, k)$为区间$[l,r]$上第$k$小的数, 则有

$\text{LEAST}(l, r, k) = \max \{x \mid \text{RANK}(l, r, x)<k\}$

因而对于询问$\text{LEAST}(l, r, x)$, 我们可以二分答案 $x$ + $\text{RANK}(l, r, x)$判断, 从而在$O(\log{M}\log^2{N})$的复杂度内完成查询, 其中$M$是元素的范围, $N$是区间总长度. 当然, 我们也可以现将数组$A[1\cdots N]$排序, 付出一个$O(N\log{N}$)的预处理复杂度, 然后便可做到单次查询$O(\log^3{N})$.

但我觉得这个复杂度还是太高了, (归并排序)这种做法应该还有优化的可能性, 有待研究.

2. 划分树

 

posted @ 2016-07-31 23:38  Pat  阅读(457)  评论(0编辑  收藏  举报