标签:
对应HDU题目:点击打开链接
3 9 5 Top 1 Rank 3 Top 7 Rank 6 Rank 8 6 2 Top 4 Top 5 7 4 Top 5 Top 2 Query 1 Rank 6
Case 1: 3 5 8 Case 2: Case 3: 3 6
有三种操作:Top x(将x提到第一位), Query x(询问元素x在第几位), Rank k (询问第k位的元素是谁)。
思路:
可以用伸展树实现。n有10^8那么大,而q只有10^5,所以应该离散化把Top和Query的操作数作为点,把他们之间的区间也作为点(区间内部的点不需要移动,所以可以这样),这样最多有200000左右的结点。
Top X :把X伸展到根,删掉X,在树的左下角添加结点X。
Query X :把X伸展到根,则sz[Left[T]] + 1就是结果
Rand X :从根开始根据情况往左右两边找
伸展树只需要保存子树的所有点(加上区间的所有点),以a[]数组的下标作为结点编号,对于Top和Query操作,可以通过二分查找确定结点编号。
C++ AC,G++ RE代码:
#include <cstdio> #include <algorithm> #include <cstdlib> #include <cstring> #include <iostream> #define N 200024 #define nil (0x7fffffff) using namespace std; int Left[N]; int Right[N]; int fa[N]; int sz[N]; //以i为根的子树的结点数 int b[N>>1]; char Q[N>>1][7]; int X[N>>1]; typedef struct { int first, second; }PAIR; PAIR a[N]; //映射数组 int len; //初始化 void Build(int pre, int &T, int x, int y) { int mid = ((x + y)>>1); T = mid; fa[T] = pre; Left[T] = Right[T] = nil; sz[T] = a[T].second; if(x == y) return; if(x < mid){ Build(T, Left[T], x, mid - 1); sz[T] += sz[Left[T]]; } if(mid < y){ Build(T, Right[T], mid + 1, y); sz[T] += sz[Right[T]]; } } void R_rotate(const int x) { const int y = fa[x]; const int z = fa[y]; const int k = Right[x]; int sx = sz[x], sy = sz[y], sk = 0; if(nil != k) sk = sz[k]; Left[y] = k; Right[x] = y; if(nil != z){ if(y == Left[z]) Left[z] = x; else Right[z] = x; } if(nil != k) fa[k] = y; fa[y] = x; fa[x] = z; sz[y] = sy - sx + sk; sz[x] = sx - sk + sz[y]; } void L_rotate(const int x) { const int y = fa[x]; const int z = fa[y]; const int k = Left[x]; int sx = sz[x], sy = sz[y], sk = 0; if(nil != k) sk = sz[k]; Right[y] = k; Left[x] = y; if(nil != z){ if(y == Right[z]) Right[z] = x; else Left[z] = x; } if(nil != k) fa[k] = y; fa[y] = x; fa[x] = z; sz[y] = sy - sx + sk; sz[x] = sx - sk + sz[y]; } int Find(int x) { int l = 1, r = len, mid; while(l <= r) { mid = ((l + r)>>1); if(a[mid].first == x) return mid; else if(a[mid].first < x) l = mid + 1; else r = mid - 1; } return -1; } void Splay(int x, int &T) { if(nil == T) return; int p, end; end = fa[T]; p = x; while(end != fa[x]) { p = fa[x]; if(end == fa[p]){ //p是根结点 if(x == Left[p]) R_rotate(x); else L_rotate(x); break; } //p不是根结点 if(x == Left[p]){ if(p == Left[fa[p]]){ R_rotate(p); //LL R_rotate(x); //LL } else{ R_rotate(x); //RL L_rotate(x); } } else{ if(p == Right[fa[p]]){ //RR L_rotate(p); L_rotate(x); } else{ //LR L_rotate(x); R_rotate(x); } } } T = x; } //在队首添加结点 void Add_at_lower_left_corner(int x, int T) { int p = T; sz[p]++; while(nil != Left[p]){ p = Left[p]; sz[p]++; } Left[p] = x; sz[x] = 1; fa[x] = p; Left[x] = Right[x] = nil; } void Delete_root(int &T) { if(nil == T) return; int l, r, x; l = Left[T]; r = Right[T]; if(nil == l && nil == r){ T = nil; return; } if(nil != l) fa[l] = nil; if(nil != r) fa[r] = nil; if(nil == l){ //没有左儿子 T = r; return; } if(nil == r){ //没有右儿子 T = l; return; } x = l; while(nil != Right[x]) x = Right[x]; //x为T的左子树的中序最大值结点(右下角结点) Splay(x, l); //把x伸展到左子树的根结点 T = l; //把左子树的根作为整棵树的根,此时T没有右子树 //接上右子树 Right[T] = r; if(nil != r){ fa[r] = T; sz[T] += sz[r];//更新结点数 } } void Top(int x, int &T) { x = Find(x); Splay(x, T); //把x结点伸展到根 if(nil == Left[T]) return; //新根没有左儿子,根即为第一个数 Delete_root(T); //删除根结点 Add_at_lower_left_corner(x, T); //在T的左下角增加结点x Splay(x, T); } int Rand(int x, int T) { if(nil == T) return 0; int sum, p; p = T; sum = (nil == Left[p] ? 0 : sz[Left[p]]) + a[p].second; while(sum != x) { if(sum > x){ if(x < sz[Left[p]] + 1) p = Left[p]; else return a[p].first - (sum - x); } else{ x -= sum; p = Right[p]; } if(nil == p) return 0; sum = (nil == Left[p] ? 0 : sz[Left[p]]) + a[p].second; } return a[p].first; } int Query(int x, int &T) { x = Find(x); if(nil == T) return 0; Splay(x, T); return (nil == Left[x] ? 0 : sz[Left[x]]) + 1; } //离散化 void Discrete(int n, int j) { int i, k; sort(b + 1, b + j + 1); k = 1; for(i = 2; i <= j; i++) //去重 if(b[i] != b[k]) b[++k] = b[i]; len = 0; for(i = 1; i <= k; i++){ if(b[i] - b[i-1] > 1){ a[++len].first = b[i] - 1; a[len].second = b[i] - b[i-1] - 1; } a[++len].first = b[i]; a[len].second = 1; } if(b[k] < n){ a[++len].first = n; a[len].second = n - b[k]; } } int main() { //freopen("in.txt","r",stdin); int T, t, n, q, w = 0; int i, j; scanf("%d", &t); while(t--) { printf("Case %d:\n", ++w); scanf("%d%d", &n, &q); j = 0; for(i = 0; i < q; i++){ scanf("%s%d", Q[i], &X[i]); if('T' == Q[i][0] || 'Q' == Q[i][0]) b[++j] = X[i]; } Discrete(n, j); //离散化 Build(nil, T, 1, len); for(i = 0; i < q; i++){ if('T' == Q[i][0]) Top(X[i], T); else if('Q' == Q[i][0]) printf("%d\n", Query(X[i], T)); else printf("%d\n", Rand(X[i], T)); } } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
Splay树 + 离散化 —— HDU 3436 Queue-jumpers
标签:
原文地址:http://blog.csdn.net/u013351484/article/details/46876883