bzoj1861: [Zjoi2006]Book 书架(平衡树)

原题链接

题目描述:小T有一个很大的书柜。这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列。她用1到的正整数给每本书都编了号。 小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

解析:算是平衡树中比较经典的题了,用splay和treap都可以解决。这里给出treap的做法。
???将每本书的位置作为权值插入treap中,记录val[x]为编号为x的书的位置。
???相比与treap的模板,要多对每一个节点记录一个ind,表示该点所表述的书的编号。
???那么对于Top操作,便是将原先书x的节点删除,再插入该节点(节点的权值要是所有数中最小的)。
???对于Bottom操作,就是将原先书x的节点删除,再插入该节点(节点的权值要是所有数中最大的)。
???对于Insert操作。若t是0,就直接continue;否则就将两个点都删除,再将两个点的信息交换后插入。
???对于Ask操作,就是输出val[x]的排名再-1。
???对于Query操作,就是输出排名为x的数的ind值。

代码如下:

#include<cstdio>
#include<algorithm>
using namespace std;

const int maxn = 80005;
int n, m, root, val[maxn];
int size[maxn << 1], lson[maxn << 1], rson[maxn << 1], c[maxn << 1], cnt, a[maxn << 1], pri[maxn << 1], ind[maxn << 1]; //数组要开两倍!
char s[10];

int read(void) {
    char c; while (c = getchar(), (c < '0' || c > '9') && c != '-'); int x = 0, y = 1;
    if (c == '-') y = -1; else x = c - '0';
    while (c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; return x * y;
}

int rand() {
    static int seed = 2333;
    return seed = (int)((((seed ^ 998244353) + 19260817ll) * 19890604ll) % 1000000007);
}

void up(int k) {
    size[k] = size[lson[k]] + size[rson[k]] + c[k];
}

void zig(int &k) {
    int v = rson[k]; rson[k] = lson[v]; lson[v] = k;
    size[v] = size[k]; up(k); k = v;
}

void zag(int &k) {
    int v = lson[k]; lson[k] = rson[v]; rson[v] = k;
    size[v] = size[k]; up(k); k = v;
}

void insert(int &k, int x, int id) {
    if (k == 0) {
      k = ++ cnt;
      size[k] = c[k] = 1; a[k] = x; pri[k] = rand(); ind[k] = id;
      return;
    }
    size[k] ++;
    if (a[k] == x) c[k] ++;
    else if (x > a[k]) {
      insert(rson[k], x, id);
      if (pri[rson[k]] < pri[k]) zig(k);
    }
    else {
      insert(lson[k], x, id);
      if (pri[lson[k]] < pri[k]) zag(k);
    }
}

void del(int &k, int x) {
    if (k == 0) return;
    if (a[k] == x) {
      if (c[k] > 1) c[k] --, size[k] --;
      else {
        if (!lson[k] || !rson[k]) k = lson[k] + rson[k];
        else if (pri[lson[k]] < pri[rson[k]]) zag(k), del(k, x);
        else zig(k), del(k, x);
      }
    }
    else if (x > a[k]) size[k] --, del(rson[k], x);
    else size[k] --, del(lson[k], x);
}

int query_rank(int k, int x) {
    if (k == 0) return 2e9;
    if (a[k] == x) return size[lson[k]] + 1;
    if (x > a[k]) return size[lson[k]] + c[k] + query_rank(rson[k], x);
    else return query_rank(lson[k], x);
}

int query_num(int k, int x) {
    if (k == 0) return 2e9;
    if (x <= size[lson[k]]) return query_num(lson[k], x);
    x -= size[lson[k]];
    if (x <= c[k]) return ind[k]; //注意这里是ind!
    x -= c[k];
    return query_num(rson[k], x);
}

int main() {
    n = read(); m = read();
      for (int i = 1; i <= n; ++ i) {
        int x = read();
        val[x] = i;
        insert(root, i, x);
      }
      for (int i = 1; i <= m; ++ i) {
        scanf("%s", s + 1);
        if (s[1] == 'Q') {
            int x = read();
            printf("%d\n", query_num(root, x));
          }
        else if (s[1] == 'T') {
          int x = read();
          del(root, val[x]);
          insert(root, 1 - i, x);
          val[x] = 1 - i;
        }
        else if (s[1] == 'B') {
          int x = read();
          del(root, val[x]);
          insert(root, n + i, x);
          val[x] = n + i;
        }
        else if (s[1] == 'A') {
          int x = read();
          printf("%d\n", query_rank(root, val[x]) - 1);
        }
        else if (s[1] == 'I') {
          int x = read(), y = read();
            if (y == 0) continue;
          int z = query_num(root, query_rank(root, val[x]) + y);
          del(root, val[x]);
          del(root, val[z]);
          insert(root, val[x], z);
          insert(root, val[z], x);
          swap(val[x], val[z]);
        }
      }
    return 0;
} 

原文地址:https://www.cnblogs.com/Gaxc/p/10127422.html

时间: 2024-10-09 13:11:00

bzoj1861: [Zjoi2006]Book 书架(平衡树)的相关文章

【平衡树】【pb_ds】 bzoj1861 [Zjoi2006]Book 书架

需要用数组记录编号为i的书的位置,和位置i处的书的编号. Code: 1 #include<cstdio> 2 #include<ext/pb_ds/assoc_container.hpp> 3 #include<ext/pb_ds/tree_policy.hpp> 4 using namespace std; 5 using namespace __gnu_cxx; 6 using namespace __gnu_pbds; 7 tree<int,null_ty

BZOJ1861 [Zjoi2006]Book 书架

从6点调到了现在22:19:07..脑子晕倒死. 用splay做,一开始怎么想也不知道该怎么play. 想了个办法,用pos[i]表示编号为i的书在树上的节点编号 s[i]表示树上的节点i代表的是哪本书. val[i]表示节点i的权值,这里的权值按照书从上到下的大小顺序来赋值,用来建树 TOP:先把树里面代表书S的那个节点删掉,然后再重新建一个节点把S放进去,这时候, 为了使新的节点在最前方,开一个mmin表示已经出现过的最小的数, 每次要把节点加入进去的时候插入几点的权值就是 --mmin:

【权值分块】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

bzoj1861 [Zjoi2006]Book 书架——splay

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1861 发现自己想splay的时候总是纠结那个点权是什么,因为splay原本是二分查找树... 但其实splay已经不是维护点权大小顺序的,它的最大作用就在于无论怎样旋转都保持着中序遍历这个相对位置不变: 所以很对应这道题,用splay进行各种操作的同时书的摆放顺序是不变的: 假设出一个'1'点.一个'n+1'点方便操作,所以整体+1: 这个建树的方法不错呢. 代码如下: #include<

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

1861: [Zjoi2006]Book 书架 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 1453  Solved: 822[Submit][Status][Discuss] Description 小T有一个很大的书柜.这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列.她用1到n的正整数给每本书都编了号. 小T在看书的时候,每次取出一本书,看完后放回书柜然后再拿下一本.由于这些书太有吸引力了,所以她看完后常常会忘记原来是放在书柜的什么位

BZOJ 1861: [Zjoi2006]Book 书架 splay

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

[Zjoi2006]Book 书架

pre.cjk { font-family: "Droid Sans Fallback", monospace } p { margin-bottom: 0.25cm; line-height: 120% } a:link { } 1861: [Zjoi2006]Book 书架 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 1581  Solved: 884[Submit][Status][Discuss] Description 小T有一

BZOJ 1861 [Zjoi2006]Book 书架 ——Splay

[题目分析] 模板题目. 首尾两个虚拟结点,十分方便操作. [代码] #include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #include <map> #include <set> #include <queue> #include <string> #include <iostream> #include

[题解]bzoj 1861 Book 书架 - Splay

1861: [Zjoi2006]Book 书架 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 1396  Solved: 803[Submit][Status][Discuss] Description 小T有一个很大的书柜.这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列.她用1到n的正整数给每本书都编了号. 小T在看书的时候,每次取出一本书,看完后放回书柜然后再拿下一本.由于这些书太有吸引力了,所以她看完后常常会忘记原来是放在书柜的什么位