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

Posted gaxc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
", 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
", 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;
} 

以上是关于bzoj1861: [Zjoi2006]Book 书架(平衡树)的主要内容,如果未能解决你的问题,请参考以下文章

[BZOJ1861][Zjoi2006]Book 书架

[bzoj1861][Zjoi2006]Book 书架_非旋转Treap

BZOJ 1861: [Zjoi2006]Book 书架 splay

BZOJ1861 [Zjoi2006]Book 书架

bzoj1861: [Zjoi2006]Book 书架

BZOJ 1861 [Zjoi2006]Book 书架 ——Splay