洛谷P2596 [ZJOI2006]书架【splay】

题目描述

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

输入样例

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

输出样例

2

9

9

7

5

3

说明

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

*******************************

题目分析:

Top

把指定元素编号旋转到根

1.若此时根节点无左子树,则直接返回

2.若此时根节点无右子树,则直接交换左右子树

3.左右子树均存在

找到当前序列中排名为size[ch[rt][0]+2的编号,设为y

(即找到当前根节点元素在序列中的的下一个元素编号,后继)

令根的左子树成为y的左子树(ch[y][0]=ch[rt][0])

Bottom

把指定元素编号旋转到根

1.若此时根节点无右子树,则直接返回

2.若此时根节点无左子树,则直接交换左右子树

3.左右子树均存在

找到当前序列中排名为size[ch[rt][0]的编号,设为y

(即找到当前根节点元素在序列中的的上一个元素编号,前驱)

令根的右子树成为y的右子树(ch[y][1]=ch[rt][1])

Insert

t=0时直接忽略操作

把指定元素编号旋转到根

若t=1,找到根节点后继,交换编号及元素信息

若t=-1,找到根节点前驱,交换编号及元素信息

Ask

把指定元素编号旋转到根

输出根节点左子树大小

Query

常规find操作

****************************************

#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<‘0‘||ss>‘9‘){if(ss==‘-‘)f=-1;ss=getchar();}
    while(ss>=‘0‘&&ss<=‘9‘){x=x*10+ss-‘0‘;ss=getchar();}
    return f*x;
}

void print(int x)
{
    if(x<0){putchar(‘-‘);x=-x;}
    if(x>9)print(x/10);
    putchar(x%10+‘0‘);
}

int n,m,rt;
int v[100010],pos[100010],size[100010];
int ch[100010][2],fa[100010],sz;
char ss[20];

void update(int p)
{
    size[p]=size[ch[p][0]]+size[ch[p][1]]+1;
}

void rotate(int& p,int x) {
    int y=fa[x],z=fa[y];
    int t=(ch[y][0]==x);
    if(y==p) p=x;
    else if(ch[z][0]==y) ch[z][0]=x;
    else ch[z][1]=x;
    fa[y]=x; fa[ch[x][t]]=y; fa[x]=z;
    ch[y][t^1]=ch[x][t]; ch[x][t]=y;
    update(y);update(x);
} 

void splay(int& p,int x)
{
    while(x!=p)
    {
        int y=fa[x],z=fa[y];
        if(y!=p)
        {
            if((ch[y][0]==x)^(ch[z][0]==y)) rotate(p,x);
            else rotate(p,y);
        }
        rotate(p,x);
    }
}

void ins(int x)
{
    v[++sz]=x; pos[x]=sz;
    size[sz]=1; ch[sz][0]=ch[sz][1]=0;
    if (sz>1)
    {
        ch[sz-1][1]=sz;fa[sz]=sz-1;
        splay(rt,sz);
    }
}

int find(int p,int k)
{
    int ss=size[ch[p][0]];
    if(ss+1==k) return p;
    else if(ss>=k) return find(ch[p][0],k);
    else return find(ch[p][1],k-ss-1);
}

void top_bottom(int d)
{
    int x=read(),y; x=pos[x];
    splay(rt,x);//指定元素旋转到根
    if(!ch[x][d]) return;//第1种情况
    if(!ch[x][d^1]) {ch[x][d^1]=ch[x][d]; ch[x][d]=0; return;}//第2种情况

    if(d==0) y=find(rt,size[ch[x][0]]+2);//元素置顶,找到根的后继
    else y=find(rt,size[ch[x][0]]);//元素置底,找到根的前驱
    fa[ch[rt][d]]=y;//合并子树
    ch[y][d]=ch[rt][d];
    ch[rt][d]=0;
    splay(rt,y);//伸展保证复杂度
}

void change()
{
    int x=read(),d=read(),y; if(d==0) return;//d=0直接忽略
    splay(rt,pos[x]);//指定元素旋转到根
    if(d==1) y=find(rt,size[ch[pos[x]][0]]+2);//元素提前,找到其前驱
    else y=find(rt,size[ch[pos[x]][0]]);//元素置后,找到其后继
    int tv=v[y],tpos=pos[x];//交换信息
    swap(pos[x],pos[tv]);
    swap(v[tpos],v[y]);
}

int get()
{
    int x=read(); x=pos[x];
    splay(rt,x);
    return size[ch[x][0]];
}

int main()
{
    n=read();m=read();rt=1;
    for(int i=1;i<=n;++i)
    {
        int x=read();ins(x);
    }

    while(m--)
    {
        scanf("%s",&ss);
        if(ss[0]==‘T‘) top_bottom(0);
        else if(ss[0]==‘B‘) top_bottom(1);
        else if(ss[0]==‘I‘) change();
        else if(ss[0]==‘A‘) print( get() ),putchar(‘\n‘);
        else if(ss[0]==‘Q‘) print( v[ find(rt,read()) ] ),putchar(‘\n‘);
    }

    return 0;
}

原文地址:https://www.cnblogs.com/niiick/p/8724138.html

时间: 2024-08-27 00:49:35

洛谷P2596 [ZJOI2006]书架【splay】的相关文章

洛谷P1848 [USACO12OPEN]书架Bookshelf

当农夫约翰闲的没事干的时候,他喜欢坐下来看书.多年过去,他已经收集了 N 本书 (1 <= N <= 100,000), 他想造一个新的书架来装所有书. 每本书 i 都有宽度 W(i) 和高度 H(i).书需要按顺序添加到一组书架上:比如说,第一层架子应该包含书籍1 ... k,第二层架子应该以第k + 1本书开始,以下如此.每层架子的总宽度最大为L(1≤L≤1,000,000,000).每层的高度等于该层上最高的书的高度,并且整个书架的高度是所有层的高度的总和,因为它们都垂直堆叠. 请帮助农

洛谷——P2676 超级书架

https://www.luogu.org/problem/show?pid=2676#sub 题目描述 Farmer John最近为奶牛们的图书馆添置了一个巨大的书架,尽管它是如此的大,但它还是几乎瞬间就被各种各样的书塞满了.现在,只有书架的顶上还留有一点空间. 所有N(1 <= N <= 20,000)头奶牛都有一个确定的身高H_i(1 <= H_i <= 10,000).设所有奶牛身高的和为S.书架的高度为B,并且保证 1 <= B <= S < 2,000

洛谷P2676 超级书架

题目描述 Farmer John最近为奶牛们的图书馆添置了一个巨大的书架,尽管它是如此的大,但它还是几乎瞬间就被各种各样的书塞满了.现在,只有书架的顶上还留有一点空间. 所有N(1 <= N <= 20,000)头奶牛都有一个确定的身高H_i(1 <= H_i <= 10,000).设所有奶牛身高的和为S.书架的高度为B,并且保证 1 <= B <= S < 2,000,000,007. 为了够到比最高的那头奶牛还要高的书架顶,奶牛们不得不象演杂技一般,一头站在另

洛谷P2677 超级书架 2

题目描述 Farmer John最近为奶牛们的图书馆添置了一个巨大的书架,尽管它是如此的大,但它还是几乎瞬间就被各种各样的书塞满了.现在,只有书架的顶上还留有一点空间. 所有N(1 <= N <= 20)头奶牛都有一个确定的身高H_i(1 <= Hi <= 1,000,000 - 好高的奶牛><). 设所有奶牛身高的和为S.书架的 高度为B,并且保证1 <= B <= S. 为了够到比最高的那头奶牛还要高的书架顶,奶牛们不得不象演杂技一般,一头站在另一头的背

洛谷 P2677 超级书架 2

P2677 超级书架 2 题目描述 Farmer John最近为奶牛们的图书馆添置了一个巨大的书架,尽管它是如此的大,但它还是几乎瞬间就被各种各样的书塞满了.现在,只有书架的顶上还留有一点空间. 所有N(1 <= N <= 20)头奶牛都有一个确定的身高H_i(1 <= H_i <= 1,000,000 - 好高的奶牛>_<).设所有奶牛身高的和为S.书架的 高度为B,并且保证1 <= B <= S. 为了够到比最高的那头奶牛还要高的书架顶,奶牛们不得不象演

洛谷P1772 [ZJOI2006]物流运输

题目描述 物流公司要把一批货物从码头A运到码头B.由于货物量比较大,需要n天才能运完.货物运输过程中一般要转停好几个码头.物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格的管理和跟踪.由于各种因素的存在,有的时候某个码头会无法装卸货物.这时候就必须修改运输路线,让货物能够按时到达目的地.但是修改路线是—件十分麻烦的事情,会带来额外的成本.因此物流公司希望能够订一个n天的运输计划,使得总成本尽可能地小. 输入输出格式 输入格式: 第一行是四个整数n(l≤n≤100).m(l≤m≤2

洛谷P2676 超级书架 题解

题目传送门 题目一看就是贪心.C++福利来了:sort. 基本思路就是:要使奶牛最少那么肯定高的奶牛先啦. 直接排序一遍(从高到矮)然后while,搞定! #include<bits/stdc++.h> #define ll long long using namespace std; ll N,B,H[20010]; bool cmp(int x,int y){return x>y;} int main(){ scanf("%lld%lld",&N,&

在洛谷3369 Treap模板题 中发现的Splay详解

本题的Splay写法(无指针Splay超详细) 前言 首先来讲...终于调出来了55555...调了整整3天..... 看到大部分大佬都是用指针来实现的Splay.小的只是按照Splay的核心思想和原理来进行的.可能会有不妥之处,还请大佬们指出,谢谢! 那么这个题解存在的意义就是让不会敲Splay的人额...会敲Splay啦... 基本思想 数据结构 对于Splay,我定义了一个class类(当成struct就行啦...个人习惯不同啦),定义名称为“Splay”. 之后在类中,我定义了Splay

[ZJOI2006]书架(权值splay)

[ZJOI2006]书架(luogu) Description 题目描述 小T有一个很大的书柜.这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列.她用1到n的正整数给每本书都编了号. 小T在看书的时候,每次取出一本书,看完后放回书柜然后再拿下一本.由于这些书太有吸引力了,所以她看完后常常会忘记原来是放在书柜的什么位置.不过小T的记忆力是非常好的,所以每次放书的时候至少能够将那本书放在拿出来时的位置附近,比如说她拿的时候这本书上面有X本书,那么放回去时这本书上面就只可能有X-1.X或X+1