花了两天时间学了一下这三种数据结构。
总结 树上的修改与询问
伸展树:
支持Spaly的平衡树。Splay操作可以让节点x旋转到树的根节点。
伸展树在区间插入,区间删除,区间翻转,区间旋转的问题中应用较多。
具体而言,我们可以以区间的下标为关键字构造伸展树。
在x与x+1中插入一段区间,我们可以通过Spaly操作完成,将x旋转至根节点,将x+1旋转至根节点的右节点,此时,x+1的左节点为空,插入该左节点即可。
删除 [ l, r ] 区间通过Splay后,直接令根节点的右节点的左节点为空即可。
翻转操作,可以类似线段树加入懒惰标记即可。
旋转区间,复杂一点可以三次翻转。优秀的见Claris代码。
其中,Spaly操作的均摊复杂度为O(log(n)).(treap的复杂度为期望复杂度O(log(n)))
1 const int N = 1e5+200; 2 int a[N]; //输入数组 3 int val[N], mn[N], tag[N], size[N], son[N][2], f[N], tot, root; 4 bool rev[N]; 5 void rev1(int x) { //翻转以x为根的子树 6 if(!x) return ; 7 swap(son[x][0], son[x][1]); 8 rev[x] ^= 1; 9 } 10 void add1(int x,int p) { //以x为根的子树 +p 11 if(!x) return ; 12 val[x] += p; 13 mn[x] += p; 14 tag[x] += p; 15 } 16 void pb(int x){ 17 if(rev[x]){ 18 rev1(son[x][0]); 19 rev1(son[x][1]); 20 rev[x] = 0; 21 } 22 if(tag[x]){ 23 add1(son[x][0], tag[x]); 24 add1(son[x][1], tag[x]); 25 tag[x] = 0; 26 } 27 } 28 void up(int x){ 29 size[x] = 1, mn[x] = val[x]; 30 if(son[x][0]){ 31 size[x] += size[son[x][0]]; 32 if(mn[x] > mn[son[x][0]]) mn[x] = mn[son[x][0]]; 33 } 34 if(son[x][1]){ 35 size[x] += size[son[x][1]]; 36 if(mn[x] > mn[son[x][1]]) mn[x] = mn[son[x][1]]; 37 } 38 } 39 void rotate(int x){ 40 int y = f[x], w = son[y][1] == x; 41 son[y][w] = son[x][w^1]; 42 if(son[x][w^1]) f[son[x][w^1]] = y; 43 if(f[y]){ 44 int z = f[y]; 45 if(son[z][0] == y) son[z][0] = x; 46 if(son[z][1] == y) son[z][1] = x; 47 } 48 f[x] = f[y]; son[x][w^1] = y; f[y] = x; up(y); 49 } 50 void splay(int x, int w){ //splay操作, 使得x的父节点为w, 注意包含端节点的情况 51 //一般通过x = kth(x)找到第x小的节点, w = kth(w) 52 int s = 1, i = x, y; a[1] = x; 53 while(f[i]) a[++s] = i = f[i]; 54 while(s) pb(a[s--]); 55 while(f[x] != w){ 56 y = f[x]; 57 if(f[y] != w){ 58 if((son[f[y]][0]==y)^(son[y][0]==x)) rotate(x); 59 else rotate(y); 60 } 61 rotate(x); 62 } 63 if(!w) root = x; 64 up(x); 65 } 66 int build(int l, int r, int fa){ 67 int x = ++tot, mid = (l+r)>>1; 68 f[x] = fa; val[x] = a[mid]; 69 if(l < mid) son[x][0] = build(l, mid-1, x); 70 if(r > mid) son[x][1] = build(mid+1, r, x); 71 up(x); 72 return x; 73 } 74 int kth(int k){ 75 int x = root, tmp; 76 while(1){ 77 pb(x); 78 tmp = size[son[x][0]]+1; 79 if(k == tmp) return x; 80 if(k < tmp) x = son[x][0]; 81 else k -= tmp, x = son[x][1]; 82 } 83 }