《算法竞赛进阶指南》黑盒子

黑盒子

黑盒子代表一个原始的数据库。

它可以用来储存整数数组,并且它拥有一个特殊变量i。

在最开始,黑盒子是空的,并且i=0。

现在对黑盒子进行一系列的操作处理,操作包括以下两种:

1、ADD(x):表示将x加入到黑盒子中。
2、GET:使i增加1,输出黑盒子中第i小的数值(即将所有数按升序排序后的第i个数)。

下面给出一个具体例子:

序号 操作 i 盒子内数(升序排列后) 输出的值
1 ADD(3) 0 3
2 GET 1 3 3
3 ADD(1) 1 1, 3
4 GET 2 1, 3 3
5 ADD(-4) 2 -4, 1, 3
6 ADD(2) 2 -4, 1, 2, 3
7 ADD(8) 2 -4, 1, 2, 3, 8
8 ADD(-1000) 2 -1000, -4, 1, 2, 3, 8
9 GET 3 -1000, -4, 1, 2, 3, 8 1
10 GET 4 -1000, -4, 1, 2, 3, 8 2
11 ADD(2) 4 -1000, -4, 1, 2, 2, 3, 8
为了方便描述,下面我们定义两个序列:

1、A(1),A(2),…,A(M):这个序列由加入到黑盒子内的所有元素按加入顺序排列后得到,上例中的A序列为(3,1,-4,2,8,-1000,2)。

2、u(1),u(2),…,u(N): 这个序列的第i项表示的是第i次GET操作时,盒子内元素的数量。上例中的u序列为(1,2,6,6)。

现在请你根据给出的序列A和u求出操作过程中输出的所有数值。

输入格式
输入包括三行。

第一行包含两个整数M和N,表示A序列和u序列的长度。

第二行包含M个整数,表示A序列的每一个元素。

第三行包含N个整数,表示u序列的每一个元素。

同行每个数之间用空格隔开。

输出格式
输出操作过程中所有GET操作输出的数值。

每个数值占一行。

数据范围
|A(i)|<=2∗109,
1≤N≤M≤30000,
对于所有p(1≤p≤N), p≤u§≤M成立

输入样例:
7 4
3 1 -4 2 8 -1000 2
1 2 6 6
输出样例:
3
3
1
2

初次看到本题是,由于一动态求第几小的数,感觉可以用单调队列的方法,但单调队列的含义是可以动态求最值,但本问题是求第几小的数,并不是最值问题;

起始本题的解决方法是用两个堆来维护,分别为大顶堆和小顶堆,就能起到动态查找第几小的元素了。

我们首先要知道大顶堆是降序的,小顶堆是升序的;即大顶堆的元素方向我们可以理解为向左的,小顶堆的元素方向是向右的这样就可以通过在中间“夹”的方法把需要找到元素找出来。

#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
const int N=30010;
int n,m;
int a[N],b[N];
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int j=1;j<=m;j++)
	    cin>>b[j];
     priority_queue<int >down;//大顶堆用于存储我们当前要求的第几小元素
     priority_queue<int ,vector<int >,greater<int > >up; 
    for(int i=1,j=1;i<=m;i++)
    {//根据b数组的元素,判读我们当前需要遍历b[i]个元素
    //其中j为a数组的指针
        
        while(j<=b[i])
        {//注意本循环过程中大顶堆始终保持其元素个数不变
        //即始终保持堆顶是当前的第几小
           if(down.empty()||a[j]>=down.top())
           	up.push(a[j]);
           //当大顶堆为空时,说明我们当前需要找到的是第一小的数
           //此时不需要插入,只需要在循环结束之后接受小顶堆的堆顶就行
           //当当前遍历到的数据大于大顶堆堆顶是第几小的性质
           //只有当当前元素小于大顶堆元素才会影响大顶堆元素当前第几小的性质
           else
           {//为保持大顶堆堆顶是第几小的性质,将大顶堆的堆顶弹出至小顶堆
           	//然后大顶堆在接受当前小的元素,这样大顶堆的堆顶就保持了当前第几小的性质
             up.push(down.top());
             down.pop();
             down.push(a[j]);
           }
           j++;//注意j指针要前移
        }	
        //循环结束之后,我们要输出的第几小的这个排序应该加1
        //所以只需要将小顶堆的堆顶弹出至大顶堆,就可以满足这个条件
        down.push(up.top());
        up.pop();
        cout<<down.top()<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值