splay伸展树基础操作(bzoj 1861: [Zjoi2006]Book 书架)
2017-07-17 02:09
417 查看
splay:一种排序树(中序遍历权值有序)
主要性质:随着访问翻转次数的增多,复杂度越来越接近logn,形态也越来越接近平衡树
主要功能:每次将要询问or删除or修改的点先一路翻转到根,然后再满足所需操作
翻转规则如下:
其中
②or③+①or④的操作为Zig-Zag
①or④+①or④的操作为Zig-Zig
具体解析看代码
参考博文:http://blog.csdn.net/changtao381/article/details/8936765
Submit: 1677 Solved: 955
[Submit][Status][Discuss]
当然也有特殊情况,比如在看书的时候突然电话响了或者有朋友来访。这时候粗心的小T会随手把书放在书柜里所有书的最上面或者最下面,然后转身离开。 久而久之,小T的书柜里的书的顺序就会越来越乱,找到特定的编号的书就变得越来越困难。于是她想请你帮她编写一个图书管理程序,处理她看书时的一些操作,以及回答她的两个提问:(1)编号为X的书在书柜的什么位置;(2)从上到下第i本书的编号是多少。
4. Ask S——询问编号为S的书的上面目前有多少本书。 5. Query S——询问从上面数起的第S本书的编号。
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
9
9
7
5
3
此题解析:
从上到下,从小到大
①②③操作:删掉对应节点并插入要求的位置
④操作:将对应节点旋转到根后求其左子树大小
⑤操作:直接从根开始Find()第S小的点
主要性质:随着访问翻转次数的增多,复杂度越来越接近logn,形态也越来越接近平衡树
主要功能:每次将要询问or删除or修改的点先一路翻转到根,然后再满足所需操作
翻转规则如下:
其中
②or③+①or④的操作为Zig-Zag
①or④+①or④的操作为Zig-Zig
具体解析看代码
参考博文:http://blog.csdn.net/changtao381/article/details/8936765
1861: [Zjoi2006]Book 书架
Time Limit: 4 Sec Memory Limit: 64 MBSubmit: 1677 Solved: 955
[Submit][Status][Discuss]
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 101 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
29
9
7
5
3
此题解析:
从上到下,从小到大
①②③操作:删掉对应节点并插入要求的位置
④操作:将对应节点旋转到根后求其左子树大小
⑤操作:直接从根开始Find()第S小的点
#include<stdio.h> #include<string.h> #define inf 1000000000 using namespace std; int n, m, root, sz, tre[80005][2], fa[80005], deep[80005], a[80005], size[80005], v[80005], pos[80005]; void Update(int k) //更新k节点的size值 { size[k] = size[tre[k][0]]+size[tre[k][1]]+1; } void Create(int l, int r, int last) //传入的是当前区间以及其"父亲" { int mid; if(l>r) return; mid = (l+r)/2; //mid作为当前节点 if(l==r) { v[mid] = a[mid]; size[mid] = 1; fa[mid] = last; if(mid<last) tre[last][0] = mid; //判断当前的mid节点是last节点的左儿子还是右儿子并连接,下同 else tre[last][1] = mid; return; } Create(l, mid-1, mid); Create(mid+1, r, mid); v[mid] = a[mid]; fa[mid] = last; Update(mid); if(mid<last) tre[last][0] = mid; else tre[last][1] = mid; } void Rotate(int x, int &k) { int l, r, y, z; y = fa[x], z = fa[y]; if(tre[y][0]==x) l = 0; else l = 1; r = l^1; if(y==k) k = x; else { if(tre[z][0]==y) tre[z][0] = x; else tre[z][1] = x; } fa[x] = z, fa[y] = x; fa[tre[x][r]] = y; tre[y][l] = tre[x][r]; tre[x][r] = y; Update(y); Update(x); } void Splay(int x, int &k) //将点x不停地向上旋,直到x代替节点k的位置 { int y, z; while(x!=k) { y = fa[x], z = fa[y]; if(y!=k) { if((tre[y][0]==x)^(tre[z][0]==y)) //对于形式②或形式③ Zig-Zag Rotate(x, k); else Rotate(y, k); //Zig-Zig } Rotate(x, k); } } int Find(int k, int rank) //寻找排名rank的点 { int l, r; l = tre[k][0], r = tre[k][1]; if(size[l]+1==rank) return k; else if(size[l]>=rank) return Find(l, rank); else return Find(r, rank-size[l]-1); } void Delete(int k) //删除节点k { int x, y; x = Find(root, k-1); //找到比k小且离k最近的节点x(这个时候k已经被翻转到根了) y = Find(root, k+1); //找到比k大且离k最近的节点y Splay(x, root); //将x一路翻转到根 Splay(y, tre[x][1]); //y作为x的右节点 tre[y][0] = 0; //因为大小在x和y之间的节点只有k,而k已经被删了,所以y没有左子树,清空 Update(y); Update(x); } void Move(int k, int val) //val表示的只是操作的类型而已 { int x, y, z, rank; z = pos[k]; Splay(z, root); rank = size[tre[z][0]]+1; Delete(rank); //其实是个先删除在插入的过程 if(val==inf) { x = Find(root, n); y = Find(root, n+1); } else if(val == -inf) { x = Find(root, 1); y = Find(root, 2); } else { x = Find(root, rank+val-1); y = Find(root, rank+val); } Splay(x, root); //一样,将x翻转到根,将y作为x的右节点 Splay(y, tre[x][1]); //因为大小在x和y之间的只有待插入的值,所以将带插入值作为y的左子树 size[z] = 1, fa[z] = y, tre[y][0] = z; Update(y); Update(x); } int main(void) { int i, S, T; char str[15]; scanf("%d%d", &n, &m); for(i=2;i<=n+1;i++) { scanf("%d", &a[i]); pos[a[i]] = i; } Create(1, n+2, 0); // 在两端加入两个边界点 root = (n+3)/2; while(m--) { scanf("%s%d", str+1, &S); switch(str[1]) { case 'T': Move(S, -inf); break; //把T放在最上面(让T的标号最小) case 'B': Move(S, inf); break; //把T放在最下面(让T的标号最大) case 'I': scanf("%d", &T); Move(S, T); break; case 'A': Splay(pos[S], root); printf("%d\n", size[tre[pos[S]][0]]-1); break; //把pos[S]节点旋到根,那么在S上面的个数显然是其左子树的size值(这里-1是因为加了两个边界点) case 'Q': printf("%d\n", v[Find(root, S+1)]); //直接查找 } } return 0; }
相关文章推荐
- BZOJ 1861: [Zjoi2006]Book 书架
- bzoj 1861 [Zjoi2006]Book 书架
- BZOJ 1861: [Zjoi2006]Book 书架 | SPlay 板题
- BZOJ 1861: [Zjoi2006]Book 书架 (splay)
- BZOJ 1861 [Zjoi2006]Book 书架 ——Splay
- [BZOJ1861] [Zjoi2006]Book 书架
- BZOJ 1861 ZJOI2006 Book 书架 Splay
- BZOJ 1861: [Zjoi2006]Book 书架
- [BZOJ1861][Zjoi2006]Book 书架(平衡树)
- BZOJ1861: [Zjoi2006]Book 书架 Splay
- BZOJ 1861: [Zjoi2006]Book 书架
- 【BZOJ 1861】 [Zjoi2006]Book 书架
- BZOJ 1861 [Zjoi2006]Book 书架
- BZOJ 1861 ZJOI2006 Book 书架 Splay
- bzoj1861 [Zjoi2006]Book 书架
- BZOJ1861: [Zjoi2006]Book 书架
- 【bzoj1861】[Zjoi2006]Book 书架 splay
- BZOJ 1861 [Zjoi2006] Book 书架
- bzoj1861: [Zjoi2006]Book 书架 平衡树 Splay
- bzoj 1861: [Zjoi2006]Book 书架