标签:
对应HDU题目:点击打开链接
8 2 CUT 3 5 4 FLIP 2 6 -1 -1
1 4 3 7 6 2 5 8
题意:
n个数一开始是1~n顺序排列,有两个操作。
CUT(a, b, c)操作表示把第a到第b个数取下放到新组成的第c个数后面;
FLIP(a, b)操作表示把第a到第b个数翻转;
比如样例: n = 8
1 2 3 4 5 6 7 8
CUT(3, 5, 4)后数列变成 1 2 6 7 3 4 5 8
FLIP(2, 6)后数列变成1 4 3 7 6 2 5 8
问m次操作后的数列为?
思路:
伸展树的基础操作,区间截断,区间翻转。
区间截断:对于CUT(a, b, c)
1)提取区间[a, b]
具体方法:Splay(a - 1, T),Splay(b +1, T->right);即把第a - 1个数旋转到根,把第b + 1个数旋转到根的右儿子;那以根的右儿子的左儿子为根的子树就是所有区间[a, b]内的值;把它剪下。
2)把第c个数旋转到根,把第c + 1个数旋转到根的右儿子;那根的右儿子的左儿子肯定是空的。
3)把剪下的子树接在根的右儿子的左儿子。
区间翻转:对于FLIP(a, b)
1)提取区间[a, b]
2)在左儿子做翻转标志(注意不是简单的赋值为1,而是要做异或操作,即原来是1的标记为0,是0的标记为1)
3)在适当的地方加Push_down向下更新(跟线段树区间更新一样)
首先要明白的一点是二叉树结点的信息是最终中序遍历(一定是按下标先后顺序)输出的值,而不是下标的值;那怎样取下标为a的结点呢?
利用每个结点的sz,它表示以该结点为子树的的所有元素个数(T->sz = T->left->sz + T->right->sz + 1)
从根结点p开始
1)如果(左子树的元素个数加上1(1表示p结点) )sum = p->left->sz + 1;if (sum == a) ,那就找到了,返回p结点
2)sum比a小,说明要往右走(p = p->right);同时a -= sum;跳到步骤1
3)sum比a大,说明要往左走(p = p->right);a无需变动;跳到步骤1
怎样书写Push_down函数
就把左右子树对调,然后取消该结点标记,把两棵子树添加标记。
注意:修改时要注意Push_down的地方,注意父亲指针;注意更新sz;用动态的方法时要注意儿子是否为空。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #define MAX(x, y) ((x) > (y) ? (x) : (y)) typedef struct TREE { int data; TREE *fa, *l, *r; int sz; //以该结点为根的树的总结点数 bool flag; //翻转标记 }Tree; bool space; void Push_down(Tree *T) { if(NULL == T) return; //左右子树对调 if(T->flag){ Tree *tmp; tmp = T->r; T->r = T->l; T->l = tmp; T->flag = 0; if(T->l) T->l->flag ^= 1; if(T->r) T->r->flag ^= 1; } } void Init(Tree *&T, int n) { int i; bool k = 0; Tree *cur, *pre; for(i = n + 1; i > -1; i--){ cur = (Tree *)malloc(sizeof(Tree)); cur->data = i; cur->fa = cur->l = cur->r = NULL; cur->sz = 1; cur->flag = 0; if(k){ cur->r = pre; pre->fa = cur; cur->sz = pre->sz + 1; } if(!k) k = 1; pre = cur; } T = cur; } void PreOrder(Tree *T) { if(NULL == T) return; printf("%d ", T->data); PreOrder(T->l); PreOrder(T->r); } void MidOrder(Tree *T, int n) { if(NULL == T) return; Push_down(T); MidOrder(T->l, n); if(T->data > 0 && T->data < n + 1){ if(space) printf(" "); else space = 1; printf("%d", T->data); } MidOrder(T->r, n); } void R_rotate(Tree *x) { Tree *y = x->fa; Tree *z = y->fa; Tree *k = x->r; int sx = x->sz, sy = y->sz, sk = 0; if(k) sk = k->sz; y->l = k; x->r = y; if(z){ if(y == z->l) z->l = x; else z->r = x; } if(k) k->fa = y; y->fa = x; x->fa = z; y->sz = sy - sx + sk; x->sz = sx - sk + y->sz; } void L_rotate(Tree *x) { Tree *y = x->fa; Tree *z = y->fa; Tree *k = x->l; int sx = x->sz, sy = y->sz, sk = 0; if(k) sk = k->sz; y->r = k; x->l = y; if(z){ if(y == z->r) z->r = x; else z->l = x; } if(k) k->fa = y; y->fa = x; x->fa = z; y->sz = sy - sx + sk; x->sz = sx - sk + y->sz; } //寻找第x个数的结点 Tree *FindTag(Tree *T, int x) { if(NULL == T) return NULL; Push_down(T); Tree *p; p = T; int sum = (p->l ? p->l->sz : 0) + 1; while(sum != x && p) { if(sum < x){ p = p->r; x -= sum; } else p = p->l; Push_down(p); sum = (p->l ? p->l->sz : 0) + 1; } Push_down(p); return p; } void Splay(int x, Tree *&T) { Push_down(T); Tree *p, *X, *end, *new_t; end = T->fa; new_t = T; if(end) new_t = T->fa; X = FindTag(new_t, x); while(X->fa != end) { p = X->fa; if(end == p->fa){ //p是根结点 if(X == p->l) R_rotate(X); else L_rotate(X); break; } //p不是根结点 if(X == p->l){ if(p == p->fa->l){ R_rotate(p); //LL R_rotate(X); //LL } else{ R_rotate(X); //RL L_rotate(X); } } else{ if(p == p->fa->r){ //RR L_rotate(p); L_rotate(X); } else{ //LR L_rotate(X); R_rotate(X); } } } T = X; } void CUT(Tree *&T, int a, int b, int c) { //取[a,b] Splay(a - 1, T); Splay(b + 1, T->r); //剪[a,b] Tree *tmp; tmp = T->r->l; tmp->fa = NULL; T->r->l = NULL; T->r->sz -= tmp->sz; T->sz -= tmp->sz; //移动第c个数到根结点,第c+1个数到根结点右儿子 //这样根结点右儿子的左儿子必然为空,就可以把剪掉的放上去 Splay(c, T); Splay(c + 1, T->r); //接[a, b] T->r->l = tmp; tmp->fa = T->r; T->r->sz += tmp->sz; T->sz += tmp->sz; } void FLIP(Tree *&T, int a, int b) { //取[a,b] Splay(a - 1, T); Splay(b + 1, T->r); //标记T->r->l T->r->l->flag ^= 1; } void FreeTree(Tree *T) { if(NULL == T) return; FreeTree(T->l); FreeTree(T->r); free(T); } int main() { //freopen("in.txt", "r", stdin); Tree *T; int n, q, a, b, c; char s[6]; while(scanf("%d%d", &n, &q), n >= 0 && q >= 0) { space = 0; T = NULL; Init(T, n); while(q--) { scanf("%s", s); if('C' == s[0]){ scanf("%d%d%d", &a, &b, &c); CUT(T, a + 1, b + 1, c + 1); } else{ scanf("%d%d", &a, &b); FLIP(T, a + 1, b + 1); } } MidOrder(T, n); printf("\n"); FreeTree(T); } return 0; }
Splay树——HDU 3487 Play with Chain
标签:
原文地址:http://blog.csdn.net/u013351484/article/details/46366881