【树状数组区间第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

 

你可能感兴趣的:(树状数组)