划分树与区间第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大神的代码注释。另外这篇文章也不太适合作为划分树的入门,建议看看推荐的两篇文章。
查询区间[l,r]中第k大的数,假设序列[1,n]总共的n个数被我们分成了两个长度大致一样的子序列A和B,长度大致都为n/2,如果我们能确定要找的数在哪个子序列中,并在该序列中是第s大,那么就可以对该序列递归查询,问题的规模就缩小了一半。这就是划分树的主要思路。
问题的描述是:区间[L,R]里面,第k大的数是多少?那么划分树的查询过程就可以描述为:
- Query(L,R,K,d)
- If L == R
- Then 答案是该序列的第L个数
- Else
- If [L,R]中第K大数,是新序列的[L,mid]区间中第S大数
- Then Query(L,mid,S,d+1)
- Else if [L,R]中第K大数是新序列的[mid+1,R]区间第R大数
- Then Query(mid+1,R,R,d+1)
现在关键的问题是如何从当前序列构造新序列,并能方便的判断要找的数在新序列哪个区间,是第几大的。如果我们知道当前序列的区间[L,R]中前m小的数都去了左区间,其他的数去了右区间,就好办了。如果K小于等于m,就在左区间找,否则就去右区间找啊。这就是查询的基本思路了。还有一个问题:当前序列区间[L,R]中第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) 编辑 收藏 举报