划分树与区间第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 }

 

 

 

posted on 2012-08-06 02:11  Lattexiaoyu  阅读(369)  评论(0编辑  收藏  举报

导航