[好题] 关键字:双堆技巧。
Link&Limit
时间限制:1000ms 空间限制:65536kb
Description
Black Box是一种原始的数据库。它可以储存一个整数数组,还有一个特别的变量i。最开始的时候Black Box是空的.而i等于0。这个Black Box要处理一串命令。
命令只有两种:
ADD(x):把x元素放进BlackBox;
CET:i加l,然后输出Blackhox中第i小的数。
记住:第i小的数,就是Black Box里的数的按从小到大的顺序排序后的第i个元素。例如:
我们来演示一下一个有11个命令的命令串。(如下图所示)
现在要求找出对于给定的命令串的最好的处理方法。ADD和GET命令分别最多200000个。现在用两个整数数组来表示命令串:
1.A(1),A(2),…A(M):一串将要被放进Black Box的元素。每个数都是绝对值不超过2000000000的整数,M ≤ 200000。例如上面的例子就是A=(3,1,一4,2,8,-1000,2)。
2.u(1),u(2),…u(N):表示第u(j)个元素被放进了Blaek Box里后就出现一个GET命令。例如上面的例子中u=(l,2,6,6)。输入数据不用判错。
Input Format
第一行,两个整数,M,N。
第二行,M个整数,表示A(l)……A(M)。
第三行,N个整数,表示u(l)…u(N)。
Output Format
输出Black Box根据命令串所得出的输出串,一个数字一行。
Sample Input #1
7 4
3 1 -4 2 8-1000 2
1 2 6 6
Sample Output #1
3
3
1
2
Hint
对于30%的数据,M≤10000;
对于50%的数据,M≤100000;
对于100%的数据,M≤200000。
使用平衡树也可以完成这道题。但是题目中有一个值得关注的细节:
"GET:i加1,然后输出Blackhox中第i小的数”
如果GET操作不是按顺序询问,而是随机询问,那么平衡树自然是首选。但是GET操作是按顺序询问的,平衡树未免有点大材小用.......
所以,我们建立两个堆,一个小根堆hmin,一个大根堆hmax。一开始读入数据时,将数据加入hmin。进行GET操作时,输出hmin的堆顶,并将其移入hmax。
这样,hmax中存有的,就是当前黑箱中最小的i个数。当最新读入的数x比hmax的堆顶y要小时,说明x在新黑箱最小的i个数之中,相当于y的位置就被x挤掉了。那么就将y移回hmin。若x比y要大,则将x加入hmin。
这种双堆的技巧,也适用于求多个连续区间的中位数。
#include <cstdio> struct heap { int h[200100],tot; heap() { tot = 0;} void add(int x) { int i = ++tot,j = i>>1; h[tot] = x; for(;i>1;i=j,j>>=1) { if(x>h[j]) {h[i] = h[j]; h[j] = x;} else break; } } void del() { int i = 1,j = 2,x = h[1] = h[tot--]; for(;j<=tot;i=j,j<<=1) { if(j<tot) if(h[j+1]>h[j]) j++; if(x<h[j]) {h[i] = h[j]; h[j] = x;} else break; } } }heap1,heap2; int n,m,list[200010]; int main() { int i,q; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) scanf("%d",&list[i]); for(i=1;m;m--) { scanf("%d",&q); for(;i<=q;i++) { heap1.add(list[i]); heap2.add(-heap1.h[1]); heap1.del(); } printf("%d\n",-heap2.h[1]); heap1.add(-heap2.h[1]); heap2.del(); } return 0; }