原帖: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 ",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 }