【ZJOI2006】书架 - Splay

题目链接

洛谷P2596

题目描述

现在书架从上到下摆了n本书,编号1-n,要求维护一个数据结构来支持下面5个操作

  • Top S 把编号为s的书放到顶部
  • Bottom S把编号为s的书放到底部
  • Insert S T如果编号为S的书上面有x本,那么把这本书放在一个位置使得它上面有X+T本书,保证 T ∈ { − 1 , 0 , 1 } T \in \{-1, 0, 1\} T{1,0,1}
  • Ask S查询编号为S的书上面有几本书
  • Query S查询第S本书的编号

数据范围 n ≤ 80000 n \leq80000 n80000

思路

使用Splay维护编号,注意编号并非有序,Splay中序遍历才是编号大小顺序。
对于第一个操作,找到对应的结点,旋转到根后,进行三个判断:

  1. 如果左子树为空,说明这本书已经在顶部,不用操作。
  2. 如果右子树为空,直接把左子树拽到右子树即可,这样左子树都比根大。
  3. 如果右子树不为空,则找到他的后继,然后把左子树拽到后继的左儿子。这样左子树比根大但是比根的右子树小。

第二个操作与第一个操作同理。
对于第三个操作直接找到这个结点,旋转到根后直接和他的前驱或后继交换即可。
对于第四个操作,就是求S是第几小的,旋转到根直接求左子树大小即可。
对于第五个操作,是求第S小的书的编号,splay直接维护就好了。

需要维护一个结点编号与书本编号的双射,因为这个题不能通过权值来找它的编号,这一点要注意。

总结

这个题调了三个小时,最后发现板子里每次找第k小的时候都会自动把找到的结点旋转到根导致之前的伸展操作被破坏。qwq嘤嘤嘤
还要注意下插入操作,每次插入接到上个结点的右儿子就行了,因为优先级是按照输入顺序决定的。

#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <set>
#include <string>
#include <vector>
#include <queue>
#define inf 0x3f3f3f3f
#define cases(t) for (int cas = 1; cas <= int(t); ++cas)
typedef long long ll;
typedef double db;
using namespace std;

#ifdef NO_ONLINE_JUDGE
#define LOG(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { cout << endl; }
template<typename T, typename... Args> void err(T a, Args... args) { cout << a << ' '; err(args...); }
#else
#define LOG(...)
#endif

const int N = 80005;
int n, m;
int a[N], mp[N];

int tot;
int ch[N][2], fa[N], size[N], val[N];

#define root ch[0][1]
inline int get(int x) { return ch[fa[x]][1] == x; }
inline int newNode(int v, int par) {
    fa[++tot] = par;
    val[tot] = v, mp[v] = tot;
    size[tot] = 1;
    return tot;
}
inline void update(int x) {
    size[x] = size[ch[x][0]] + size[ch[x][1]] + 1;
}
void rotate(int x) {
    int y = fa[x], z = fa[y];
    int yson = get(x), zson = get(y);
    int k = ch[x][yson ^ 1];
    fa[x] = z, ch[z][zson] = x;
    fa[k] = y, ch[y][yson] = k;
    fa[y] = x, ch[x][yson ^ 1] = y;
    update(y), update(x);
}
void splay(int x, int to) {
    to = fa[to];
    while (fa[x] != to) {
        int y = fa[x];
        if (fa[y] == to) rotate(x);
        else if (get(x) == get(y))   rotate(y), rotate(x);
        else rotate(x), rotate(x);
    }
}
void insert(int v) {
    int now = root;
    if (root == 0) {
        root = newNode(v, 0);
        return;
    }
    while (1) {
        size[now]++;
        if (val[now] == v) {
            splay(now, root);
            break;
        }
        if (!ch[now][1]) {
            int p = newNode(v, now);
            ch[now][1] = p;
            splay(p, root);
            break;
        }
        now = ch[now][1];
    }
}
int kth(int k) {
    int now = root;
    while (now) {
        int left = size[now] - size[ch[now][1]];
        if (k > size[ch[now][0]] && k <= left)   break;
        if (k > left) {
            now = ch[now][1];
            k -= left;
        } else {
            now = ch[now][0];
        }
    }
    // splay(now, root); 这里不需要旋转
    return now;
}

int s, t;
char op[20];

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", a + i);
        insert(a[i]);
    }
    while (m--) {
        scanf("%s%d", op, &s);
        if (op[0] == 'T') {
            int x = mp[s];
            splay(x, root);
            if (!ch[x][0])   continue;
            if (!ch[x][1])   ch[x][1] = ch[x][0], ch[x][0] = 0;
            else {
                int y = kth(size[ch[x][0]] + 2);
                fa[ch[x][0]] = y, ch[y][0] = ch[x][0];
                ch[x][0] = 0;
                splay(y, x);
            }
        } else if (op[0] == 'B') {
            int x = mp[s];
            splay(x, root);
            if (!ch[x][1])   continue;
            if (!ch[x][0])   ch[x][0] = ch[x][1], ch[x][1] = 0;
            else {
                int y = kth(size[ch[x][0]]);
                fa[ch[x][1]] = y, ch[y][1] = ch[x][1];
                ch[x][1] = 0;
                splay(y, x);
            }
        } else if (op[0] == 'I') {
            scanf("%d", &t);
            int x = mp[s];
            splay(x, root);
            if (t == 1) {
                int y = kth(size[ch[x][0]] + 2);
                swap(mp[val[y]], mp[s]);
                swap(val[y], val[mp[val[y]]]);
            } else if (t == -1) {
                int y = kth(size[ch[x][0]]);
                swap(mp[val[y]], mp[s]);
                swap(val[y], val[mp[val[y]]]);
            }
        } else if (op[0] == 'A') {
            int x = mp[s];
            splay(x, root);
            printf("%d\n", size[ch[x][0]]);
        } else {
            printf("%d\n", val[kth(s)]);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值