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;
}