ZJOI 2006 书架
题意
请你维护一个长度为\(n\)的排列,要求支持以下操作
- 把权值为\(v\)的元素放到序列的首端
- 把权值为\(v\)的元素放到序列的尾端
- 把权值为\(v\)的元素和它前一个或后一个元素进行交换
- 询问权值为\(v\)的元素之前有多少个元素
- 询问第\(k\)个元素的权值
解法
可以用无旋Treap来维护区间
和Splay一样,Treap可以用来维护区间是由于它的下标的中序遍历是整个序列的映射
我们建立由权值到位置的映射pos,我们就能直接找到权值为\(v\)的元素在Treap上对应的结点
除了无旋Treap的基础操作外,我们发现由于维护的现在是下标而不是权值,如果要求序列中的第\(k\)个数我们并不能用传统的查询第\(k\)大的方式进行
我们维护每个节点的父亲,对于权值为\(v\)的元素,我们暴力向上跳父亲,统计所有比当前数小的元素个数
这样我们就得到了权值为\(v\)的元素在排列中的位置
注意,由于是维护序列,这里的Split操作是按照树的大小进行分裂的,若我们对整颗树进行参数为\(k\)的Split操作,这时候我们得到的大小为\(k\)的子树即为排列的前\(k\)个数
代码
#include <ctime>
#include <cstdio>
#include <cstdlib>
using namespace std;
const int N = 1e5 + 10;
int read();
int n, m;
int pos[N];
char op[10];
struct FHQ_Treap {
#define ls(x) t[x].ch[0]
#define rs(x) t[x].ch[1]
#define fa(x) t[x].fa
int cnt;
int root, a, b, c, d;
struct node {
int siz, val, rnd, fa;
int ch[2];
node() { ch[0] = ch[1] = 0; }
} t[N];
FHQ_Treap() : cnt(0), root(0) {}
int newnode(int v) {
++cnt;
t[cnt].siz = 1, t[cnt].val = v, t[cnt].rnd = rand() << 15 | rand();
return cnt;
}
void update(int x) {
t[x].siz = t[ls(x)].siz + t[rs(x)].siz + 1;
fa(ls(x)) = fa(rs(x)) = x;
}
void split(int x, int k, int& lt, int& rt) {
if (!x)
return lt = rt = 0, void();
if (k <= t[ls(x)].siz)
rt = x, split(ls(x), k, lt, ls(x));
else
lt = x, split(rs(x), k - t[ls(x)].siz - 1, rs(x), rt);
update(x);
}
int merge(int x, int y) {
if (!x || !y)
return x | y;
if (t[x].rnd < t[y].rnd) {
rs(x) = merge(rs(x), y);
return update(x), x;
} else {
ls(y) = merge(x, ls(y));
return update(y), y;
}
}
int kth(int k) {
split(root, k - 1, a, b);
split(b, 1, b, c);
int res = t[b].val;
root = merge(a, merge(b, c));
return res;
}
int find(int x) {
int res = t[ls(x)].siz + 1;
for (; x; x = fa(x))
if (x == rs(fa(x))) res += t[ls(fa(x))].siz + 1;
return res;
}
void insert(int v) {
root = merge(root, newnode(v));
}
void top(int x) {
split(root, x - 1, a, b);
split(b, 1, b, c);
root = merge(b, merge(a, c));
}
void bottom(int x) {
split(root, x - 1, a, b);
split(b, 1, b, c);
root = merge(merge(a, c), b);
}
void change(int x, int t) {
if (!t)
return;
t = (~t) ? 1 : 2;
split(root, x - t, a, b);
split(b, 2, b, c);
split(b, 1, b, d);
root = merge(a, merge(merge(d, b), c));
}
void debug(int x) {
printf("%d ls: %d rs: %d sz: %d\n", x, ls(x), rs(x), t[x].siz);
if (ls(x)) debug(ls(x));
if (rs(x)) debug(rs(x));
}
#undef ls
#undef rs
#undef fa
} tr;
int main() {
srand(time(NULL));
n = read(), m = read();
for (int i = 1; i <= n; ++i) {
int v = read();
tr.insert(v);
pos[v] = i;
}
// tr.debug(tr.root);
for (int i = 1; i <= m; ++i) {
scanf("%s", op + 1);
int S = read();
int rnk = tr.find(pos[S]);
switch (op[1]) {
case 'T':
tr.top(rnk);
break;
case 'B':
tr.bottom(rnk);
break;
case 'I':
tr.change(rnk, read());
break;
case 'A':
printf("%d\n", rnk - 1);
break;
case 'Q':
printf("%d\n", tr.kth(S));
break;
}
}
return 0;
}
int read() {
int x = 0, f = 1, c = getchar();
while (c < '0' || c > '9') c == '-' ? f = -1, c = getchar() : c = getchar();
while (c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x * f;
}