zoukankan      html  css  js  c++  java
  • 数据结构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
    ", BS(l, r, k, n)-1);
    57     }
    58 }

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

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

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

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

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

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

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

    2. 划分树

  • 相关阅读:
    vim键盘
    Maven 插件
    Maven 快照
    Maven 常用命令
    Maven POM
    Maven 依赖机制
    Maven 中央仓库
    Maven 本地仓库
    Maven 安装配置
    Maven 简介
  • 原文地址:https://www.cnblogs.com/Patt/p/5724316.html
Copyright © 2011-2022 走看看