[ZJOI2006]书架(权值splay)

Posted hsez-cyx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ZJOI2006]书架(权值splay)相关的知识,希望对你有一定的参考价值。

[ZJOI2006]书架(luogu)

Description

题目描述

小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语句你应该输出一行,一个数,代表询问的答案。

Solution

  • top/bottom:把S节点移到根,再把根的左子树、右子树移为后继/前驱的左儿子/右儿子
  • insert:把S节点移到根,再与前驱/后继交换信息
  • ask:把S节点移到根,输出左子树的size
  • query:BST中第S大的点对应的编号

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int N=8e4+10;
struct node
{
    int size,v,ch[2],fa;
    void clear()
    {
        size=v=fa=ch[0]=ch[1]=0;
    }
}f[N];
int id[N],n,m,rt,S,T;
char c[20];
void push_up(int g)
{
    f[g].size=f[f[g].ch[0]].size+f[f[g].ch[1]].size+1;
}
int get(int x)
{
    return x==f[f[x].fa].ch[1];
}
void rotate(int x)
{
    int y=f[x].fa,z=f[y].fa;
    int wh=get(x);
    f[f[x].ch[wh^1]].fa=y;
    f[y].ch[wh]=f[x].ch[wh^1];
    f[x].ch[wh^1]=y;
    f[x].fa=z,f[y].fa=x;
    if(z) f[z].ch[y==f[z].ch[1]]=x;
    push_up(y),push_up(x);
}
void splay(int x)
{
    for(int fx=f[x].fa;fx=f[x].fa,fx;rotate(x))
        if(f[fx].fa) rotate(get(x)==get(fx)?fx:x);
    rt=x;
}
int build(int l,int r,int fa)
{
    int x=(l+r)>>1;
    f[x].fa=fa;
    if(l==r) return x;
    if(l<x) f[x].ch[0]=build(l,x-1,x);
    if(x<r) f[x].ch[1]=build(x+1,r,x);
    push_up(x);
    return x;
}
int pre()
{
    int x=f[rt].ch[0];
    while(f[x].ch[1]) x=f[x].ch[1];
    return x;
}
int nxt()
{
    int x=f[rt].ch[1];
    while(f[x].ch[0]) x=f[x].ch[0];
    return x;
}
void change(int x,int t)
{
    splay(x);
    if(!f[x].ch[t]) return ;
    if(!f[x].ch[t^1])
    {
        swap(f[x].ch[0],f[x].ch[1]);
        return;
    }
    int y=(t==1)?pre():nxt(),z=f[x].ch[t];
    f[y].ch[t]=z,f[z].fa=y,f[x].ch[t]=0;
    while(y) push_up(y),y=f[y].fa;
}
void insert(int x,int t)
{
    if(t==0) return ;
    splay(x);
    int y=(t==1)?nxt():pre();
    swap(f[x].v,f[y].v);
    swap(id[f[x].v],id[f[y].v]);
}
int kth(int k)
{
    int x=rt;
    while(1)
    {
        int lc=f[x].ch[0],rc=f[x].ch[1];
        if(f[lc].size+1==k) return x;
        else if(f[lc].size>=k) x=lc;
        else k-=f[lc].size+1,x=rc;
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&f[i].v);
        id[f[i].v]=i,f[i].size=1;
    }
    rt=build(1,n,0);
/*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本书的编号。*/
    while(m--)
    {
        scanf("%s",c);
        scanf("%d",&S);
        if(c[0]==T) change(id[S],0);
        else if(c[0]==B) change(id[S],1);
        else if(c[0]==I)
        {
            scanf("%d",&T);
            insert(id[S],T);
        }
        else if(c[0]==A)
        {
            splay(id[S]);
            printf("%d
",f[f[rt].ch[0]].size);
        }
        else if(c[0]==Q) printf("%d
",f[kth(S)].v);
    }
    return 0;
}

 

 

 

 

以上是关于[ZJOI2006]书架(权值splay)的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 1861 [Zjoi2006]Book 书架 ——Splay

BZOJ 1861: [Zjoi2006]Book 书架 (splay)

题解 [ZJOI2006]书架 (Splay)

bzoj1861 [Zjoi2006]Book 书架——splay

BZOJ 1861: [Zjoi2006]Book 书架 splay

1861. [ZJOI2006]书架平衡树-splay