[codevs2573/洛谷P1801]黑匣子

题目描述:
我们使用黑匣子的一个简单模型。它能存放一个整数序列和一个特别的变量i。在初始时刻,黑匣子为空且i等于0。这个黑匣子能执行一系列的命令。有两类命令:
ADD(x):把元素x放入黑匣子;
GET:把i加1的同时,输出黑匣子内所有整数中第i小的数。牢记第i小的数是当黑匣子中的元素已非降序排序后位于第i位的元素。

现需要一个有效的算法处理给定的一系列命令。ADD和GET命令的总数至多个有30000个。定义ADD命令的个数为M个,GET命令的个数为N个。

1.A(1),A(2),…A(M):一串将要被放进Black Box的元素。每个数都是绝对值不超过2000000000的整数。
2.u(1),u(2),…u(N):表示第u(j)个元素被放进了Blaek Box里后就出现一个GET命令。例如上面的例子中u=(l,2,6,6)。输入数据不用判错

输入格式:
第一行,两个整数,M,N。
第二行,M个整数,表示A(l)……A(M)。
第三行,N个整数,表示u(l)…u(N)。

输出格式:
输出Black Box根据命令串所得出的输出串,一个数字一行。

题解:第一种方法就是用平衡树硬解,很管用。
第二种方法:开左右两个堆,左堆为大根堆,右堆为小根堆(方便转移),一个变量KAF用于记录当前答案,维护这两个堆:1.左堆的所有元素都小于等于KAF; 2.右堆所有元素都大于等于KAF ; 3.左堆的元素数量维持在i-1。

#第一种方法

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<time.h>
using namespace std;
int l[300004],r[300004],s[300004],w[300004],rnd[300004],sz=0;
int neta=0,n,m,root=0;
int a[300004],u[300004];
void update(int x){s[x]=s[l[x]]+s[r[x]]+1;}
void lturn(int &x){
     int t=r[x];
     r[x]=l[t];
     l[t]=x;
     s[t]=s[x];
     update(x);
     x=t;
}
void rturn(int &x){
     int t=l[x];
     l[x]=r[t];
     r[t]=x;
     s[t]=s[x];
     update(x);
     x=t;
}
void add(int &k,int x){
     if(k==0){
         k=++sz;w[k]=x;
         rnd[k]=rand();s[k]=1;return;
     }
     s[k]++;
     if(x<w[k]){
         add(l[k],x);
         if(rnd[k]>rnd[l[k]])rturn(k);
     }
     else {
         add(r[k],x);
         if(rnd[k]>rnd[r[k]])lturn(k);
     }
}
int find(int k,int x){
    if(x==0)return -1;
    if(x<=s[l[k]])return find(l[k],x);
    else if(x>s[l[k]]+1)return find(r[k],x-s[l[k]]-1);
    else return w[k];
}
int main(){
    scanf("%d%d",&m,&n);
    for(int i=1;i<=m;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)scanf("%d",&u[i]);
    int p=1;
    for(int i=1;i<=m;i++){
        add(root,a[i]);
        while(i==u[p]){
            printf("%d\n",find(root,p));
            ++p;
        }
    }
    return 0;
}

#第二种方法

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<queue>
#include<vector>
using namespace std;
priority_queue<int,vector<int>,less<int> >q1;
priority_queue<int,vector<int>,greater<int> >q2;
int n,m,a[200004],KAF,ok=-1,ep=0;
int g[200004];
void repute(){
     if(q2.empty()){
        ok=-1;
        q1.push(KAF);
        return;
     }
     while(q1.size()<ep){
        q1.push(KAF);KAF=q2.top();q2.pop();
     }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=m;i++){
        int x;
        scanf("%d",&x);
        g[x]++;
    }
    for(int i=1;i<=n;i++){
        if(ok == -1){
            ok = 1;
            KAF = a[i];
        }
        else{
            if(a[i]<KAF){
                q2.push(KAF);
                KAF = a[i];
            }
            else q2.push(a[i]);
        }
        if(!q1.empty()&&a[i]<q1.top()){
            int x=q1.top();
            q1.pop();
            KAF=x;
            q1.push(a[i]);
        }
        while(g[i]--){
            printf("%d\n",KAF);
            ++ep;
            repute();
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值