zoukankan      html  css  js  c++  java
  • 划分树与区间第k大值

      先在这里推荐两篇不错的文章:

      HH大神的文章,代码和注释写的非常漂亮,很容易读懂:

     http://www.notonlysuccess.com/index.php/divide-tree/#more-142

      MatoNo1的文章,建树的部分讲的挺细致的:

     http://www.cppblog.com/MatoNo1/archive/2011/06/27/149604.aspx

      前几天的比赛遇到了要求区间第k大值的问题,听说要用划分树做,于是决定学习划分树去。在网上找了几篇文章看,慢慢弄清楚了划分树是怎么回事,在这里就只写写我自己的一些理解吧,算是一些补充,写的不清楚的还请见谅,或者参看HH大神的代码注释。另外这篇文章也不太适合作为划分树的入门,建议看看推荐的两篇文章。

      查询区间[lr]中第k大的数,假设序列[1n]总共的n个数被我们分成了两个长度大致一样的子序列AB,长度大致都为n/2,如果我们能确定要找的数在哪个子序列中,并在该序列中是第s大,那么就可以对该序列递归查询,问题的规模就缩小了一半。这就是划分树的主要思路

      问题的描述是:区间[LR]里面,第k大的数是多少?那么划分树的查询过程就可以描述为:

    •   QueryLRKd
    •     If  L == R
    •       Then 答案是该序列的第L个数
    •     Else
    •       If  [LR]中第K大数,是新序列的[Lmid]区间中第S大数
    •         Then QueryLmidSd+1
    •       Else if [LR]中第K大数是新序列的[mid+1R]区间第R大数
    •         Then Querymid+1RRd+1

      现在关键的问题是如何从当前序列构造新序列,并能方便的判断要找的数在新序列哪个区间,是第几大的。如果我们知道当前序列的区间[LR]中前m小的数都去了左区间,其他的数去了右区间,就好办了。如果K小于等于m,就在左区间找,否则就去右区间找啊。这就是查询的基本思路了。还有一个问题:当前序列区间[LR]中第K大的数是新序列某区间中第几大的。这些说来就麻烦了,先不说了,看代码吧。

      我的代码就是根据HH的代码写的,除了注释相似度太高了,建议把HH的代码当作模板,还有注释,比赛时看看也好啊。

     

     1 //zzy2012.8.6
     2 #include<cstdio>
     3 #include<iostream>
     4 #include<algorithm>
     5 #define NUM 10000
     6 using namespace std;
     7 
     8 typedef struct{
     9     int l,r,mid;
    10 }node;
    11 
    12 node tree[4*NUM+400];
    13 int n,q,sorted[NUM + 1], val[20][NUM + 1], Lnum[20][NUM + 1];
    14 
    15 void structTree(int l,int r,int d,int idx){
    16     tree[idx].l = l;
    17     tree[idx].r = r;
    18     tree[idx].mid = (l+r)>>1;
    19     if(l == r)
    20         return ; //到达叶子节点
    21     int mid;
    22     mid = tree[idx].mid;
    23     int lsame = mid - l + 1;
    24     for(int i=l; i<=r; i++)
    25         if(val[d][i] < sorted[mid])
    26             lsame --;
    27     int p,q,same = 0;
    28     p = tree[idx].l;
    29     q = tree[idx].mid + 1;
    30     for(int i=l; i<=r; i++){
    31         if(i == l)
    32             Lnum[d][i] = 0;
    33         else
    34             Lnum[d][i] = Lnum[d][i-1];
    35         if(val[d][i] < sorted[mid]){
    36             val[d+1][p++] = val[d][i];
    37             Lnum[d][i] ++;
    38         }
    39         else if(val[d][i] > sorted[mid]){
    40             val[d+1][q++] = val[d][i];
    41         }
    42         else{
    43             if(same < lsame){
    44                 same ++;
    45                 val[d+1][p++] = val[d][i];
    46                 Lnum[d][i] ++;
    47             }
    48             else
    49                 val[d+1][q++] = val[d][i];
    50         }
    51     }
    52     structTree(l,mid,d+1,idx<<1);
    53     structTree(mid+1,r,d+1,idx<<1|1);
    54 }
    55 
    56 int query(int l,int r,int k,int d,int idx){
    57     if(l == r)
    58         return val[d][l];
    59     int n1,n2;
    60     if(l == tree[idx].l){
    61         n1 = 0;
    62         n2 = Lnum[d][r];
    63     }
    64     else{
    65         n1 = Lnum[d][l-1];
    66         n2 = Lnum[d][r] - n1;
    67     }
    68     if(n2 >= k){
    69         int newL = tree[idx].l + n1;
    70         int newR = tree[idx].l + n1 + n2 -1;
    71         return query(newL,newR,k,d+1,idx<<1);
    72     }
    73     else{
    74         int newL = tree[idx].mid + 1 + (l - tree[idx].l - n1);
    75         int newR = newL + (r - l + 1 - n2) - 1;
    76         return query(newL,newR,k - n2,d+1,idx<<1|1);
    77     }
    78 }
    79 
    80 int main()
    81 {
    82     while(scanf("%d %d",&n,&q)!=EOF){
    83         for(int i=1; i<=n; i++){
    84             scanf("%d",&val[0][i]);
    85             sorted[i] = val[0][i];
    86         }
    87         sort(sorted+1,sorted+1+n);
    88         structTree(1,n,0,1); //区间为[1,n],在第0层,tree中下标为1的结点
    89         for(int i=1; i<=q; i++){
    90             int l,r,k;
    91             scanf("%d %d %d",&l,&r,&k);
    92             int ans;
    93             ans = query(l,r,k,0,1);
    94             printf("%d\n",ans);
    95         }
    96     }
    97     return 0;
    98 }

     

     

  • 相关阅读:
    CodeForces 687B Remainders Game
    CodeForces 689D Friends and Subsequences
    CSU 1810 Reverse
    生成树收录
    吃奶酪
    带逆向思维的并查集
    中位数定理
    种类并查集(关押犯人)
    带权并查集
    分层图
  • 原文地址:https://www.cnblogs.com/Lattexiaoyu/p/2624513.html
Copyright © 2011-2022 走看看