洛谷P3850 [TJOI2007]书架

洛谷P3850 [TJOI2007]书架

题目描述

  Knuth先生家里有个精致的书架,书架上有N本书,如今他想学到更多的知识,于是又买来了M本不同的新书。现在他要把新买的书依次插入到书架中,他已经把每本书要插入的位置标记好了,并且相应的将它们放好。由于Knuth年龄已大,过几天他已经记不清某些位置上放的到底是什么书了,请问你能帮助他吗?

输入输出格式

输入格式:

  输入文件的第一行为整数N,接下来N行分别是书架上依次放着的N本书的书名(书名由不含空格的字符串构成,长度不超过10)。下一行将要输入一个整数M,接下来的M行分别为这本书的书名和要插入的位置。下一行将要输入一个整数Q,接下来共有Q次询问,每行都是一个整数表示询问的位置。(书架上位置的编号从0开始)

输出格式:

  输出Q行,每行对应着相应查询位置的书名。

思路:

  针对一开始的每一个已经排好的书,正常地建树,节点的权值可以设为该书的编号(其实在这道题里,节点的权值并没有什么意义)
  对于新插入的节点,假设该节点插入在k位置,那么先将平衡树的第k个节点伸展到树顶,然后再把平衡树的第k-1个节点伸展到根节点的左子树。此时,很显然第k-1个节点的右子树一定为空,这时候把新来的书插在这个位置。
  最后对于每一个询问,直接查第k个节点就行了。
  为了防止各种奇怪的问题,我在建树的时候还特意插入了最小节点和最大节点

以下是代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 1000010
int ch[MAXN][2],val[MAXN],par[MAXN],cnt[MAXN],sizes[MAXN];
char names[MAXN][15];
int i,j,k,m,n,ncnt,root,q;
int chk(int x){
    return ch[par[x]][1]==x;
}
void pushup(int x){
    sizes[x]=sizes[ch[x][0]]+sizes[ch[x][1]]+cnt[x];
}
void rotate(int x){
    int y=par[x],z=par[y],k=chk(x),w=ch[x][!k];
    ch[y][k]=w,par[w]=y;
    ch[z][chk(y)]=x,par[x]=z;
    ch[x][!k]=y,par[y]=x;
    pushup(y),pushup(x);
}
void splay(int x,int goal=0){
    while(par[x]!=goal){
        int y=par[x],z=par[y];
        if(z!=goal){
            if(chk(x)==chk(y)) rotate(y);
            else rotate(x);
        }
        rotate(x);
    }
    if(!goal) root=x;
}
void find(int v){
    if(!root) return;
    int cur=root;
    while(val[ch[cur][v>val[cur]]]&&v!=val[cur]){
        cur=ch[cur][v>val[cur]];
    }
    splay(cur);
}
void insert(int v){
    int cur=root,p=0;
    while(cur&&val[cur]!=v){
        p=cur;
        cur=ch[cur][v>val[cur]];
    }
    if(cur){
        cnt[cur]++;
    }else{
        cur=++ncnt;
        if(p) ch[p][v>val[p]]=cur;
        ch[cur][0]=ch[cur][1]=0;
        val[cur]=v,par[cur]=p;
        cnt[cur]=sizes[cur]=1;
    }
    splay(cur);
}
int kth(int k){
    int cur=root;
    while(true){
        if(ch[cur][0]&&sizes[ch[cur][0]]>=k){
            cur=ch[cur][0];
        }else{
            if(sizes[ch[cur][0]]+cnt[cur]<k){
                k-=sizes[ch[cur][0]]+cnt[cur];
                cur=ch[cur][1];
            }else{
                return cur;
            }
        }
    }
}
int ranks(int v){
    find(v);
    return sizes[ch[root][0]]+1;
}
int pre(int v){
    find(v);
    if(val[root]<v) return root;
    int cur=ch[root][0];
    while(ch[cur][1]) cur=ch[cur][1];
    return cur;
}
int succ(int v){
    find(v);
    if(val[root]>v) return root;
    int cur=ch[root][1];
    while(ch[cur][0]) cur=ch[cur][0];
    return cur;
}
void remove(int v){
    int last=pre(v),Next=succ(v);
    splay(last),splay(Next,last);
    int del=ch[Next][0];
    if(cnt[del]>1){
        cnt[del]--;
        splay(del);
    }else{
        ch[Next][0]=0;
        splay(Next);
    }
}
int main(){
    ncnt=0; root=0;
    insert(-1),insert(1000000000);
    scanf("%d",&n);
    for(i=1;i<=n;i++) {
        scanf("%s",names[i]);
        insert(i);
    }
    scanf("%d",&m);
    for(i=1;i<=m;i++){
        scanf("%s%d",names[n+i],&k);
        k=k+2;
        int id=kth(k);
        splay(id);
        int sid=kth(k-1);
        splay(sid,id);
        ch[sid][1]=++ncnt;
        ch[ncnt][0]=ch[ncnt][1]=0;
        par[ncnt]=sid;
        val[ncnt]=k;
        cnt[ncnt]=sizes[ncnt]=1;
        pushup(sid),pushup(id);
    }
    scanf("%d",&q);
    for(i=1;i<=q;i++){
        scanf("%d",&k);
        int id=kth(k+2);
        printf("%s\n",names[id-2]);
    }
    return 0;
}

 

posted @ 2018-11-02 22:34  EternalBlue  阅读(182)  评论(0编辑  收藏  举报