【ZJOI2006】书架 - Splay
作者:互联网
题目链接
题目描述
现在书架从上到下摆了n本书,编号1-n,要求维护一个数据结构来支持下面5个操作
Top S
把编号为s的书放到顶部Bottom S
把编号为s的书放到底部Insert S T
如果编号为S的书上面有x本,那么把这本书放在一个位置使得它上面有X+T本书,保证T∈{−1,0,1}Ask S
查询编号为S的书上面有几本书Query S
查询第S本书的编号
数据范围 n≤80000
思路
使用Splay维护编号,注意编号并非有序,Splay中序遍历才是编号大小顺序。
对于第一个操作,找到对应的结点,旋转到根后,进行三个判断:
- 如果左子树为空,说明这本书已经在顶部,不用操作。
- 如果右子树为空,直接把左子树拽到右子树即可,这样左子树都比根大。
- 如果右子树不为空,则找到他的后继,然后把左子树拽到后继的左儿子。这样左子树比根大但是比根的右子树小。
第二个操作与第一个操作同理。
对于第三个操作直接找到这个结点,旋转到根后直接和他的前驱或后继交换即可。
对于第四个操作,就是求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;
}
标签:左子,结点,include,书架,Splay,编号,ZJOI2006,操作,now 来源: https://blog.csdn.net/weixin_43135318/article/details/101052745