【BZOJ 1861】Book 书架
Description
小T有一个很大的书柜。这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列。她用1到n的正整数给每本书都编了号。 小T在看书的时候,每次取出一本书,看完后放回书柜然后再拿下一本。由于这些书太有吸引力了,所以她看完后常常会忘记原来是放在书柜的什么位置。不过小T的记忆力是非常好的,所以每次放书的时候至少能够将那本书放在拿出来时的位置附近,比如说她拿的时候这本书上面有X本书,那么放回去时这本书上面就只可能有X-1、X或X+1本书。 当然也有特殊情况,比如在看书的时候突然电话响了或者有朋友来访。这时候粗心的小T会随手把书放在书柜里所有书的最上面或者最下面,然后转身离开。 久而久之,小T的书柜里的书的顺序就会越来越乱,找到特定的编号的书就变得越来越困难。于是她想请你帮她编写一个图书管理程序,处理她看书时的一些操作,以及回答她的两个提问:(1)编号为X的书在书柜的什么位置;(2)从上到下第i本书的编号是多少。
Input
第一行有两个数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本书的编号。
Output
对于每一条Ask或Query语句你应该输出一行,一个数,代表询问的答案。
Sample Input
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
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
Sample Output
2
9
9
7
5
3
9
9
7
5
3
HINT
数据范围
100%的数据,n,m < = 80000
分析:
先看到数据范围80000,很明显要求的时间复杂度是O(NlogN),很快想到是用二叉树结构。
题目中Top和Bottom功能,可以把节点从树中删除,再把树变成它的左儿子或右儿子,这样它就是最顶上或最底下的了。Insert功能,可以看作是把节点和它的前驱或后继调换位置。Ask操作就是把节点旋到根,看它左儿子的大小。Query就是直接找第k小的节点。
刚看到这到题的时候,第i本书我直接用树的第i个节点来表示,以为这样会方便,实际上写起来很麻烦,规规矩据地写插入删除是可以的(第一次写过了,但是代码非常长,时间效率也不高,或许是我写的不好),但是实际上那样代码还是挺复杂的,如果就根据题目给的要求写函数,又不好调(这是第二次写的,没调好),所以只好重写一次。
最后就重新又写了一边,用一个数组id[i]来表示第i本书对应树上的节点,这样就轻松愉快多了。
(据说这道题可以直接调用stl。。QAQ)
代码:
1 #include <cstdio> 2 #define mx 200000 3 int n, m, a, b, bk[mx], id[mx]; 4 char str[10]; 5 int f[mx], c[mx][2], s[mx], k[mx], size, root; 6 7 inline void pushup (int x) 8 { 9 s[x] = s[c[x][0]] + s[c[x][1]] + 1; 10 } 11 12 inline int rotate (int i) 13 { 14 int fa = f[i], d = (c[fa][1] == i); 15 f[i] = f[fa], fa > 0 ? c[f[fa]][c[f[fa]][1] == fa] = i : 0; 16 (c[fa][d] = c[i][!d]) ? f[c[i][!d]] = fa : 0; 17 pushup (c[f[fa] = i][!d] = fa); 18 return i; 19 } 20 21 inline void splay (int i, int p) 22 { 23 for (int fa = f[i]; fa != p; fa = f[rotate (i)]) 24 f[fa] != p ? rotate (c[fa][1] == i ^ c[f[fa]][1] == fa ? i : fa): 0; 25 pushup (i); 26 if (f[i] == 0) root = i; 27 } 28 29 int build (int fa, int l, int r) 30 { 31 if (l > r) return 0; 32 int m = (l + r) >> 1; 33 int i = ++size; 34 f[i] = fa; 35 k[i] = bk[m]; 36 id[bk[m]] = i; 37 c[i][0] = build (i, l, m - 1); 38 c[i][1] = build (i, m + 1, r); 39 pushup (i); 40 return i; 41 } 42 43 int check (int t, int i) 44 { 45 if (c[i][t] == 0) return i; 46 return check (t, c[i][t]); 47 } 48 49 int ask (int i, int key) 50 { 51 int t = s[c[i][0]]; 52 if (key <= t) return ask (c[i][0], key); 53 if (key > t + 1) return ask (c[i][1], key - t - 1); 54 return k[i]; 55 } 56 57 void exchange (int t, int i) 58 { 59 splay (i, 0); 60 if (c[i][t] == 0) return; 61 int x = check (!t, c[i][t]); 62 k[i] ^= k[x]; 63 k[x] ^= k[i]; 64 k[i] ^= k[x]; // swap 65 id[k[i]] = i; 66 id[k[x]] = x; 67 } 68 69 void move (int t, int i) 70 { 71 splay (i, 0); 72 if (c[i][1] > 0) 73 { 74 int x = check (0, c[i][1]); 75 splay (x, i); 76 c[x][0] = c[i][0]; 77 f[c[x][0]] = x; 78 pushup (x); 79 root = x; 80 }else 81 { 82 f[c[i][0]] = 0; 83 root = c[i][0]; 84 } 85 f[i] = c[i][0] = c[i][1] = 0; 86 f[root] = i; 87 c[i][t] = 0; 88 c[i][!t] = root; 89 pushup (i); 90 root = i; 91 splay (i, 0); 92 } 93 94 int main () 95 { 96 scanf ("%d %d", &n, &m); 97 for (int i = 1; i <= n; i++) 98 scanf ("%d", &bk[i]); 99 root = build (0, 1, n); 100 for (int i = 0; i < m; i++) 101 { 102 scanf ("%s %d", str, &a); 103 switch (str[0]) 104 { 105 case 'T': 106 move (0, id[a]); 107 break; 108 case 'B': 109 move (1, id[a]); 110 break; 111 case 'I': 112 scanf ("%d", &b); 113 if (b != 0) exchange (b == 1, id[a]); 114 break; 115 case 'A': 116 splay (id[a], 0); 117 printf ("%d\n", s[c[id[a]][0]]); 118 break; 119 case 'Q': 120 printf ("%d\n", ask (root, a)); 121 break; 122 } 123 } 124 }
Your eyes light up the world when you smile.