题目描述

Black Box是一种原始的数据库。它可以储存一个整数数组,还有一个特别的变量i。最开始的时候Black Box是空的.而i等于0。这个Black Box要处理一串命令。
命令只有两种:
ADD(x):把x元素放进BlackBox;
GET:i加1,然后输出BlackBox中第i小的数。
记住:第i小的数,就是Black Box里的数的按从小到大的顺序排序后的第i个元素。例如:
我们来演示一下一个有11个命令的命令串。(如下图所示)
(图挂了)
现在要求找出对于给定的命令串的最好的处理方法。ADD和GET命令分别最多200000个。现在用两个整数数组来表示命令串:
1. A(1)A(2),…A(M):一串将要被放进Black Box的元素。每个数都是绝对值不超过2000000000的整数,M200000。例如上面的例子就是A=(3,1,4,2,8,1000,2)
2. u(1),u(2),u(N):表示第u(j)个元素被放进了Black Box里后就出现一个GET命令。例如上面的例子中u=(1,2,6,6)。输入数据不用判错。

输入输出格式

输入格式:

第一行,两个整数,MN
第二行,M个整数,表示A(1)...A(M)
第三行,N个整数,表示u(l)...u(N)

输出格式:

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

输入输出样例

输入样例#1:

7 4
3 1 -4 2 8 -1000 2
1 2 6 6

输出样例#1:

3
3
1
2

说明

对于30%的数据,M10000
对于50%的数据,M100000
对于100%的数据,M200000

思路

有两种方法:
第一种:强行splay大法,直接按照题目意思在splay上操作。(splay可以换成其他的BST)
第二种:标准方法(堆),既然询问的k是递增的,那么可以把前k1小的数丢到一个大根堆中,剩下的数丢到一个小根堆中,操作步骤:
1. 每加入一个数,先丢到大根堆中,再将大根堆中最大的数取出来丢到小根堆中;
2. 询问第k大的数,只需要输出小根堆中的数,然后丢到大根堆中。
3. 正确性?加入的时候,如果加入的数是加入后的数列中前k小的,那么大根堆中就是前k小的所有数,应当去除第k大的数;如果不是前k小的,那么大根堆中就是前k1小的数加上加入的数,加入的数一定是大根堆中最大的,去除堆顶放入小根堆。
4. 询问的时候,小根堆中最小的数一定是第k大的,而将其取出后由于k需要加1为下一次询问做准备,那么应当将第k大转移到大根堆中。

代码(splay版)

#include <cstdio>

const int maxn=200000;

struct splay_tree
{
  int fa[maxn+10],son[2][maxn+10],val[maxn+10],size[maxn+10],tot,root;

  int t(int x)
  {
    return son[1][fa[x]]==x;
  }

  int updata(int x)
  {
    size[x]=1;
    if(son[0][x])
      {
        size[x]+=size[son[0][x]];
      }
    if(son[1][x])
      {
        size[x]+=size[son[1][x]];
      }
    return 0;
  }

  int rotate(int x)
  {
    int k=t(x),f=fa[x];
    fa[x]=fa[f];
    if(fa[f])
      {
        son[t(f)][fa[f]]=x;
      }
    son[k][f]=son[!k][x];
    if(son[!k][x])
      {
        fa[son[!k][x]]=f;
      }
    son[!k][x]=f;
    fa[f]=x;
    updata(f);
    updata(x);
    return 0;
  }

  int splay(int x,int c)
  {
    while(fa[x]!=c)
      {
        int f=fa[x];
        if(fa[f]==c)
          {
            rotate(x);
          }
        else if(t(x)==t(f))
          {
            rotate(f);
            rotate(x);
          }
        else
          {
            rotate(x);
            rotate(x);
          }
      }
    if(!c)
      {
        root=x;
      }
    return 0;
  }

  int newnode(int x)
  {
    ++tot;
    val[tot]=x;
    size[tot]=1;
    return 0;
  }

  int ins(int x)
  {
    newnode(x);
    if(!root)
      {
        root=tot;
        fa[tot]=son[0][tot]=son[1][tot]=0;
        return 0;
      }
    int now=root;
    while(now)
      {
        int p=val[now]<x;
        if(!son[p][now])
          {
            son[p][now]=tot;
            fa[tot]=now;
            son[0][tot]=son[1][tot]=0;
            break;
          }
        now=son[p][now];
      }
    splay(tot,0);
    return 0;
  }

  int getkth(int x)
  {
    int now=root;
    while(now)
      {
        if(size[son[0][now]]+1==x)
          {
            break;
          }
        else if(size[son[0][now]]+1<x)
          {
            x-=size[son[0][now]]+1;
            now=son[1][now];
          }
        else
          {
            now=son[0][now];
          }
      }
    splay(now,0);
    return val[now];
  }
};

splay_tree st;
int n,m,a[maxn+10],b[maxn+10],now=1;

int main()
{
  scanf("%d%d",&n,&m);
  for(register int i=1; i<=n; ++i)
    {
      scanf("%d",&a[i]);
    }
  for(register int i=1; i<=m; ++i)
    {
      scanf("%d",&b[i]);
    }
  for(register int i=1; i<=n; ++i)
    {
      st.ins(a[i]);
      while((now<=m)&&(b[now]==i))
        {
          printf("%d\n",st.getkth(now));
          ++now;
        }
    }
  return 0;
}

代码(堆版)

#include <cstdio>
#include <ext/pb_ds/priority_queue.hpp>//pbds神教,速度比STL堆快,支持两堆的合并

const int maxn=200000;

__gnu_pbds::priority_queue<long long,std::greater<long long> > box;//建立一个小根堆
__gnu_pbds::priority_queue<long long> w;//建立一个大根堆
long long sw,m,n,now;
long long a[maxn+10],u[maxn+10];

int main()
{
    now=1;
    scanf("%lld%lld",&m,&n);
    for(int i=1; i<=m; i++)
    {
        scanf("%lld",&a[i]);
    }
    for(int i=1; i<=n; i++)
    {
        scanf("%lld",&u[i]);
    }
    for(int i=1; i<=m; i++)
    {
        w.push(a[i]);
        box.push(w.top());
        w.pop();
        while(i==u[now])
        {
            printf("%lld\n",box.top());
            w.push(box.top());
            box.pop();
            now++;
        }
    }
    return 0;
}