【转载】【树状数组区间第K大/小】

原帖:http://www.cnblogs.com/zgmf_x20a/archive/2008/11/15/1334109.html

回顾树状数组的定义,注意到有如下两条性质:
一,c[ans]=sum of A[ans-lowbit(ans)+1 ... ans];
二,当ans=2^k时,
 c[ans]=sum of A[1 ... ans];

下面说明findK(k)如何运作:
1,设置边界条件ans,ans'<maxn且cnt<=k;
2,初始化cnt=c[ans],其中ans=2^k且k为满足边界条件的最大整数;
3,找到满足边界条件的最大的ans'使得ans'-lowbit(ans')=ans,即ans'满足c[ans']=A[ans+1 .. ans'](根据性质一),只要将c[ans']累加到cnt中(此时cnt=sum of A[1 ... ans'],根据性质二),cnt便可以作为k的逼近值;
4,重复第3步直到cnt已无法再逼近k,此时ans刚好比解小1,返回ans+1。

因此findk(k)的实质就是二分逼近

 1 /**********************************
 2 
 3 树状数组实现查找K小的元素
 4 
 5                   经典。
 6 
 7 限制:数据范围在1<<20 以内
 8 
 9 ***********************************/
10 
11 #include <iostream>
12 
13 using namespace std;
14 
15 
16 
17 #define maxn 1<<20
18 
19 int n,k;
20 
21 int c[maxn];
22 
23 
24 
25 int lowbit(int x){
26 
27     return x&-x;
28 
29 }
30 
31 
32 
33 void insert(int x,int t){
34 
35        while(x<maxn){
36 
37           c[x]+=t;
38 
39           x+=lowbit(x);    
40 
41        }
42 
43 }
44 
45 int find(int k){
46 
47     int cnt=0,ans=0;
48 
49     for(int i=20;i>=0;i--){
50 
51         ans+=(1<<i);
52 
53         if(ans>=maxn || cnt+c[ans]>=k)ans-=(1<<i);
54 
55         else cnt+=c[ans];
56 
57     }
58 
59     return ans+1;
60 
61 }
62 
63 void input(){
64 
65        memset(c,0,sizeof(c));
66 
67        int t;
68 
69        scanf("%d%d",&n,&k);
70 
71        for(int i=0;i<n;i++){    
72 
73             scanf("%d",&t);
74 
75             insert(t,1);
76 
77        }
78 
79        printf("%d\n",find(k));
80 
81 }
82 
83 int main(){
84 
85     int cases;
86 
87     scanf("%d",&cases);
88 
89     while(cases--){
90 
91         input();
92 
93     }
94 
95     return 0;
96 
97 }
View Code

 

posted @ 2015-03-07 15:26  TCtower  阅读(205)  评论(0编辑  收藏  举报