取球游戏 树状数组 + 二分



【例题5】给定N(N <= 100000)个编号为1-N的球,将它们乱序丢入一个“神奇的容器”中,作者会在丢的同时询问其中编号第K大的那个球,“神奇的容器”都能够从容作答,并且将那个球给吐出来,然后下次又可以继续往里丟。
       现在要你来模拟这个神奇的容器的功能。可以抽象成两种操作:
       1. put x                 向容器中放入一个编号为x的球;
       2. query K             询问容器中第K大的那个球,并且将那个球从容器中去除(保证K<容器中球的总数);
      
       这个问题其实就是一维Median Filter的原型了,只是那个问题的K = r+1,而这里的K是用户输入的一个常量。所谓二分模型就是在求和的过程中,利用求和函数的单调性进行二分枚举。

       对于操作1,只是单纯地执行add(x, 1)即可;而对于操作2,我们要看第K大的数满足什么性质,由于这里的数字不会有重复,所以一个显而易见的性质就是一定有K-1个数大于它,假设它的值为x,那么必然满足下面的等式:sum(N) - sum( x ) == K-1,然而,满足这个等式的x可能并不止一个,需要找到最小的那个。

  二分:
                                int t;
				while(l <= r){
					//printf("l = %d r = %d\n",l,r);
					m = (l+r)/2;
					if(check(m)){
						r = m-1;
						t = m; //这里用mid记录最后一个满足条件的位置
					}
					else{
						l = m+1;
					}
				}


输入:

5 3
put 8
put 7
put 3
put 2
query

end

输出:

3

#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int C[N+1];
int k,n;
int lowBit(int i){
	return i&(-i);
}
int getSum(int i){
	int sum = 0;
	while(i){
		sum += C[i];
		i -= lowBit(i);
	}
	return sum;
 }
int add(int i,int x){
	while(i <= N){
		C[i] += x;
		i += lowBit(i);
	}
}
bool check(int i){
	return getSum(N) - getSum(i) <= k-1; 
}
int main()
	{
	//	freopen("C:/Users/zhangwei/Desktop/input.txt","r",stdin);
		char s1[10];
		scanf("%d%d",&n,&k);
		int x,k = 0;
		while(scanf("%s",s1) == 1 && s1[0] != 'e'){
			scanf("%d",&x);
			if(s1[0] == 'p'){
				add(x,1);
			}
			else if(s1[0] == 'q'){
				int l = 1,r = N,m;
				int t;
				while(l <= r){
					//printf("l = %d r = %d\n",l,r);
					m = (l+r)/2;
					if(check(m)){
						r = m-1;
						t = m;
					}
					else{
						l = m+1;
					}
				}
				add(t,-1);	
				printf("第%d次取出的是%d号球\n",++k,t);
			} 
		}
		
		return 0;	
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值