http://acm.hdu.edu.cn/showproblem.php?pid=2852
题意:给出三种操作,
0 在容器中插入一个数。
1 在容器中删除一个数。
2 求出容器中大于a的第k大元素。
思路:二分+树状数组
树状数组的特点就是对点更新,成段求和,而且常数非常小。原始
的树状数组只有两种操作,在某点插入一个数 和 求1到i的所有数的和。
这道题目一共有三种操作,但是实质上其实只有两种:插入和询问。插入
操作和删除操作可以视为一种,只不过一个是将标记+1,另一个是-1,而
插入的数对应于树状数组的下标,这样就可以在log(n)的时间内完成插入
和删除。求大于a的k大元素,可以通过二分枚举答案来完成,枚举的是当
前答案在树状数组中的位置,设为m,然后对v[a+1] v[m]求和就是小
于等于m的数的个数,这一步可以用树状数组的求和操作来完成,然后根据
和k的比较来调整m的位置。询问的复杂度也是log(n)的。
以上转自:http://www.cppblog.com/menjitianya/archive/2011/03/31/143100.html
代码:
#include<stdio.h>
#include<string.h>
const int MAXN = 100010 ;
int Q ;
int C[MAXN] ;
int num[MAXN] ;
int lowbit(int i){
return i & ( -i ) ;
}
void add(int i, int a){
while(i < MAXN){
C[i] += a ;
i += lowbit(i) ;
}
}
int sum(int i){
int res = 0 ;
while(i >= 1){
res += C[i] ;
i -= lowbit(i) ;
}
return res ;
}
void Bsearch(int a, int k){
int low = a + 1, high = MAXN - 1 ;
int res1 = sum(a) ;
while(low < high){
int mid = (low + high) >> 1 ;
int res2 = sum(mid) ;
if(res2 - res1 < k){
low = mid + 1 ;
}
else{
high = mid ;
}
}
if(low == MAXN -1){
printf("Not Find!\n");
}
else
printf("%d\n",low);
}
int main(){
int a,b,c;
while(scanf("%d",&Q) == 1){
memset(C, 0 ,sizeof(C));
memset(num, 0 ,sizeof(num));
for(int i=0;i<Q;i++){
scanf("%d",&a);
if(a==0){
scanf("%d",&b) ;
num[b]++ ;
add(b,1) ;
}
else if(a == 1){
scanf("%d",&b);
if(num[b] == 0){
printf("No Elment!\n");
continue ;
}
num[b] -- ;
add(b,-1) ;
}
else{
scanf("%d%d",&b,&c) ;
Bsearch(b,c);
}
}
}
return 0;
}