[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\n",f[f[rt].ch[0]].size);
        }
        else if(c[0]==‘Q‘) printf("%d\n",f[kth(S)].v);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/hsez-cyx/p/12248558.html

时间: 2024-10-11 03:00:48

[ZJOI2006]书架(权值splay)的相关文章

【权值分块】bzoj1861 [Zjoi2006]Book 书架

权值分块……rank3……没什么好说的. 1 #include<cstdio> 2 #include<cmath> 3 #include<algorithm> 4 using namespace std; 5 int n,sz,sum,x,y,l[501],r[501],Min,Max,sumv[501],num[250001],m,v[250001],p[250001]; 6 bool b[250001]; 7 char op[6],c; 8 int Num,CH[1

P2234 [HNOI2002]营业额统计 (权值线段树)

P2234 [HNOI2002]营业额统计 题目描述 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额.分析营业情况是一项相当复杂的工作.由于节假日,大减价或者是其他情况的时候,营业额会出现一定的波动,当然一定的波动是能够接受的,但是在某些时候营业额突变得很高或是很低,这就证明公司此时的经营状况出现了问题.经济管理学上定义了一种最小波动值来衡量这种情况: 当最小波动值越大

【贪心】【二维偏序】【权值分块】bzoj1691 [Usaco2007 Dec]挑剔的美食家

既然题目中的要求满足二维偏序,那么我们很自然地想到将所有东西(草和牛)都读进来之后,对一维(美味度)排序,然后在另一维(价值)中取当前最小的. 于是,Splay.mutiset.权值分块什么的都支持查询后继呢. 1 #include<cstdio> 2 #include<cmath> 3 #include<algorithm> 4 using namespace std; 5 int Num,CH[12],f,c; 6 inline void R(int &x)

wikioi 1514 and ZJOI2006 书架

1514 书架 0人推荐 收藏 发题解 提交代码 报错 题目描述 输入描述 输出描述 样例输入 样例输出 提示 题目描述 Description 小 T有一个很大的书柜.这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列.她用 1 到 n 的正整数给每本书都编了号.     小 T 在看书的时候,每次取出一本书,看完后放回书柜然后再拿下一本.由于这些书太有吸引力了,所以她看完后常常会忘记原来是放在书柜的什么位置.不过小 T 的记忆力是非常好的,所以每次放书的时候至少能够将那本书放在拿出来时

【权值分块】bzoj3224 Tyvj 1728 普通平衡树

权值分块和权值线段树的思想一致,离散化之后可以代替平衡树的部分功能. 部分操作的时间复杂度: 插入 删除 全局排名 全局K大 前驱 后继 全局最值 O(1) O(1) O(sqrt(n)) O(sqrt(n)) O(sqrt(n)) O(sqrt(n)) O(sqrt(n)) 当然,因为要离散化,所以只能离线. 代码很短,很快,比我的Splay短一倍,快一倍,现在在bzoj上rank6. 1 #include<cstdio> 2 #include<algorithm> 3 #inc

【权值线段树】离散化介绍 (+利用 线段树 求逆序对)

先介绍一下离散化 桶排大家应该知道,就是开一个数组(下标为数值,记录了该数值的出现次数)然后遍历过去如果出现次数不为零,那就输出这些数字,理论时间复杂度可以达到O(N)但是由于内存限制,不能开很大的数组. 然而 如果某个数列中的数字不要求大小确定,只要求这些数字有相对的大小就够了的话,离散化就有了用武之地 举个例子:数列 3 8 7 5 2000000000000000 我们发现有几个数之间差距很大,但是我们用不到数值的大小,只要求相对大小,那怎么办呢? 观察下面的数列: 1 4 3 2 5 真

[bzoj3932][CQOI2015]任务查询系统-题解[主席树][权值线段树]

Description 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第Ei秒后结束(第Si秒和Ei秒任务也在运行 ),其优先级为Pi.同一时间可能有多个任务同时执行,它们的优先级可能相同,也可能不同.调度系统会经常向 查询系统询问,第Xi秒正在运行的任务中,优先级最小的Ki个任务(即将任务按照优先级从小到大排序后取前Ki个 )的优先级之和是多少.特别的,如

[NOIP2014]联合权值

描述 无向连通图G有n个点,n-1条边.点从1到n依次编号,编号为i的点的权值为Wi  ,每条边的长度均为1.图上两点(u, v)的距离定义为u点到v点的最短距离.对于图G上的点对(u, v),若它们的距离为2,则它们之间会产生Wu×Wv的联合权值. 请问图G上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少? 输入格式 输入文件名为link.in. 第一行包含1个整数n. 接下来n-1行,每行包含2个用空格隔开的正整数u.v,表示编号为u和编号为v的点之间有边相连.

优先队列实现哈弗曼最小权值

建立哈弗曼树要求我们每次都选频率权值最小的点构成节点,即权值小的点在树的深处,权值大的点在树的浅处,根据节点选择的特点,我们可以把节点的值放在优先队列中,包括新形成的节点. 我们先定义优先队列的优先级别. 1 struct cmp 2 { 3 bool operator()(const int &a,const int &b) 4 { 5 return a>b; 6 } 7 };//最小值优先出队 然后就是实现的整个程序. #include<stdio.h> #inclu