[ZJOI2006]书架 Treap

题目描述

小T有一个很大的书柜。这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列。她用1到n的正整数给每本书都编了号。

小T在看书的时候,每次取出一本书,看完后放回书柜然后再拿下一本。由于这些书太有吸引力了,所以她看完后常常会忘记原来是放在书柜的什么位置。不过小T的记忆力是非常好的,所以每次放书的时候至少能够将那本书放在拿出来时的位置附近,比如说她拿的时候这本书上面有X本书,那么放回去时这本书上面就只可能有X-1、X或X+1本书。

当然也有特殊情况,比如在看书的时候突然电话响了或者有朋友来访。这时候粗心的小T会随手把书放在书柜里所有书的最上面或者最下面,然后转身离开。

久而久之,小T的书柜里的书的顺序就会越来越乱,找到特定的编号的书就变得越来越困难。于是她想请你帮她编写一个图书管理程序,处理她看书时的一些操作,以及回答她的两个提问:(1)编号为X的书在书柜的什么位置;(2)从上到下第i本书的编号是多少。

输入输出格式

输入格式:
第一行有两个数n,m,分别表示书的个数以及命令的条数;第二行为n个正整数:第i个数表示初始时从上至下第i个位置放置的书的编号;第三行到m+2行,每行一条命令。命令有5种形式:

1. Top S——表示把编号为S的书房在最上面。

2. Bottom S——表示把编号为S的书房在最下面。

3. Insert S T——T∈{-1,0,1},若编号为S的书上面有X本书,则这条命令表示把这本书放回去后它的上面有X+T本书;

4. Ask S——询问编号为S的书的上面目前有多少本书。

5. Query S——询问从上面数起的第S本书的编号。

输出格式:
对于每一条Ask或Query语句你应该输出一行,一个数,代表询问的答案。

输入输出样例

输入样例#1:
10 10
1 3 2 7 5 8 10 4 9 6
Query 3
Top 5
Ask 6
Bottom 3
Ask 3
Top 6
Insert 4 -1
Query 5
Query 2
Ask 2
输出样例#1:
2
9
9
7
5
3
说明

100%的数据,n,m <= 80000

调试一万年系列又一题
这题主要是要给每本书另外定一个优先级作为v,用数组a来记下它们的优先级,再在树里面另开一个值记编号就好了,记住,它们的优先级并不等于它们现在所在的位置
所以插入的应该是a[x]而不是x
top操作就是把这本书的优先级变为最小再插入回去,bottom操作就是把这本书的优先级变为最大再插入回去,insert就是把两本书的优先级交换再插回去
query x 对应 kth(x),ask x 对应 find(a[x])
以上

下面是代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define ll int
using namespace std;
const int N=80010;
ll n,a[N],now,m,maxn,minn;

struct node
{
    node* ch[2];ll s,r,v,w;
    node(int v):v(v){ch[0]=ch[1]=NULL;s=1;r=rand();w=now;}
    void maintain() 
    { 
        s=1; 
        if(ch[0] != NULL) s+=ch[0]->s; 
        if(ch[1] != NULL) s+=ch[1]->s; 
    }
    int cmp(int x){if(v == x) return -1;return v < x;}
};
node* rt;

void rotate(node* &o,int d)
{
    node* k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o;
    o->maintain();k->maintain();o=k;
}

void ins(node* &o,int x)
{
    if(o == NULL) {o=new node(x);return ;}
    int d=(o->v < x);ins(o->ch[d],x);
    if(o->ch[d]->r > o->r) rotate(o,d^1);
    o->maintain();
}

void del(node* &o,int x)
{
    if(o == NULL) return ;
    int d=o->cmp(x);
    if(d == -1)
    {
        if(o->ch[0] != NULL && o->ch[1] != NULL)
        {
            int d2=(o->ch[0]->r > o->ch[1]->r);
            rotate(o,d2);del(o->ch[d2],x);
        }
        else {if(o->ch[0] == NULL) o=o->ch[1];else o=o->ch[0];}
    }
    else del(o->ch[d],x);
    if(o != NULL) o->maintain();
}

void kth(node* &o,int k,int &ans)
{
    if(o == NULL || k <=0 || k > o->s) return ;
    int ss=(o->ch[0] == NULL ? 0 : o->ch[0]->s);
    if(k == ss+1) {ans=o->w;return ;}
    if(k <= ss) kth(o->ch[0],k,ans);
    else kth(o->ch[1],k-ss-1,ans);
}

int rank(node* &o,int x)
{
    if(o == NULL) return 0;
    int d=o->cmp(x),ss=(o->ch[0] == NULL ? 0 : o->ch[0]->s);
    if(d == -1) return ss+1;
    if(d == 0) return rank(o->ch[0],x);
    return rank(o->ch[1],x)+ss+1;
}

int read(){
    int out=0,f=1;char c=getchar(); 
    while(c < '0' || c > '9')  
    {if(c == '-') f=-1;c=getchar();}
    while(c >= '0' && c <= '9' ) 
    {out=(out<<1)+(out<<3)+c-'0';c=getchar();} 
    return out*f;
}

void solve()
{
    n=read(),m=read(),maxn=n,minn=1;
    for(int i=1;i<=n;i++)
    {
        now=read();
        ins(rt,i);
        a[now]=i;
    }
    for(int i=1;i<=m;i++)
    {
        string cmd;
        cin>>cmd;
        if(cmd == "Query")
        {
            int k=read(),ans;kth(rt,k,ans);
            printf("%d\n",ans);
        }
        if(cmd == "Ask")
        {
            int x=read();
            printf("%d\n",rank(rt,a[x])-1);
        }
        if(cmd == "Top")
        {
            int x=read();
            del(rt,a[x]);
            a[x]=--minn;
            now=x;ins(rt,a[x]);
        }
        if(cmd == "Bottom")
        {
            int x=read();
            del(rt,a[x]);
            a[x]=++maxn;
            now=x;ins(rt,a[x]);
        }
        if(cmd == "Insert")
        {
            int x=read(),t=read();
            if(t)
            {
                int rank1=rank(rt,a[x]),rank2=rank1+t;
                int x2;kth(rt,rank2,x2);
                del(rt,a[x]);del(rt,a[x2]);
                a[x]+=a[x2];a[x2]=a[x]-a[x2];a[x]-=a[x2];
                now=x;ins(rt,a[x]);now=x2;ins(rt,a[x2]);
            }
        }
    }
}

int main()
{
    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值