对应HDU题目:点击打开链接
6 3 4 5 1 6 2 4 3 3 2 1 0
4 6 4 5 6 6 4 2 4 4
给n个数,每次将第i(1 <= i <= n)个位置到第i小的数所在位置之间的数进行翻转,输出的是第i小的数所在的位置
思路:
首先按元素升序对下标排序:
排序前
下标:1 2 3 4 5 6
元素:3 4 5 1 6 2
排序后
元素:1 2 3 4 5 6
下标:4 6 1 2 3 5伸展数记录下标的值,初始时中序遍历是1~n(其实数组型伸展树的结点号就是下标),这样按顺序每次把排序后的下标伸展到根,然后给左子树加个翻转标志,输出sz[T] + i(sz[T]表示以T为根的子树的结点数,i从1开始),最后把根删除掉就行了(删除方法是把T(T为整棵树的根)的左子树右下角的结点翻转到左子树的根,并成为新的T,这时右子树肯定为空,接上原T的右子树)
注意数组型伸展操作splay(x, T)是可以直接定位结点位置的,所以不能直接向上伸展,因为上面可能有翻转标志没有下传,所以要先沿x走到T,并记录路径,再从T沿路径把可能有的标记下传到x后才可以做伸展。
C++可AC,G++就RE的代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define N 100005 #define nil (0x7fffffff) typedef struct { int id, val; }Node; Node a[N], t[N]; int Left[N]; int Right[N]; int fa[N]; int sz[N]; //以i为根的子树的结点数 bool flag[N]; int next[N]; //记录从待翻转结点到根的路径 void Push_down(int T) { if(nil == T) return; if(flag[T]){ int tmp = Right[T]; Right[T] = Left[T]; Left[T] = tmp; flag[T] = 0; if(nil != Left[T]) flag[Left[T]] ^= 1; if(nil != Right[T]) flag[Right[T]] ^= 1; } } //初始为一条链 void Init(int &T, int n) { bool k = 0; int cur, pre; for(cur = n; cur > 0; cur--){ fa[cur] = Left[cur] = Right[cur] = nil; sz[cur] = 1; flag[cur] = 0; if(k){ Right[cur] = pre; fa[pre] = cur; sz[cur] = sz[pre] + 1; } if(!k) k = 1; pre = cur; } T = cur + 1; } 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]; } void Splay(int x, int &T) { if(nil == T) return; int p, end; end = fa[T]; p = x; while(T != p){ //记录从x到T的路径 next[fa[p]] = p; p = fa[p]; } for(p = T; ; p = next[p]){ //沿路径下传翻转标记 Push_down(p); if(p == x) break; } 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 Merge_sort(Node *A, int l, int r, Node *T) //[l, r) { if(r - l < 2) return; int m = l + (r - l) / 2; Merge_sort(A, l, m, T); Merge_sort(A, m, r, T); int p = l, i = l, q = m; while(p < m || q < r) { if(q >= r || (p < m && A[p].val <= A[q].val)){ T[i].val = A[p].val; T[i++].id = A[p++].id; } else{ T[i].val = A[q].val; T[i++].id = A[q++].id; } } for(i = l; i < r; i++){ A[i].val = T[i].val; A[i].id = T[i].id; } } void Delete(int &T) { Push_down(T); 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; } x = l; Push_down(x); while(nil != Right[x]){ x = Right[x]; //x为T的左子树的中序最大值结点 Push_down(x); } Splay(x, l); //把x伸展到左子树的根结点 T = l; //把左子树的根作为整棵树的根,此时T没有右子树 //接上右子树 Right[T] = r; sz[T] += sz[r];//更新结点数 if(nil != r) fa[r] = T; } int main() { //freopen("in.txt","r",stdin); int n, i; int T; while(scanf("%d", &n), n) { T = nil; Init(T, n); for(i = 1; i <= n; i++){ scanf("%d", &a[i].val); a[i].id = i; } Merge_sort(a, 1, n + 1, t); for(i = 1; i <= n; i++){ Splay(a[i].id, T); if(nil != Left[T]) flag[Left[T]] ^= 1; //翻转左子树 printf("%d", sz[Left[T]] + i); //输出位置 if(i < n) printf(" "); else printf("\n"); Delete(T); //删除根结点 } } return 0; }