BBST

最近学习了一下各种BBST,简单总结一下。

Splay Tree:区间操作强无敌…对[l,r)区间的操作,把l-1结点splay到树根,r结点splay到根的右结点,则根的右结点的左子树就是区间[l,r),然后对这棵子树进行操作。一个splay操作的均摊复杂度为O(logn)。

常见操作:[l,r)区间每个数加a,每个结点维护一个加的数值的lazy_tag

[l,r)区间翻转,每个结点维护一个表示是否翻转的标记

在第k个元素后插入元素或区间,若是区间,则要先建成一棵bst,把第k个结点splay到树根,第k+1个结点splay到根的右孩子,把需要插入的子树接到右孩子的左孩子。

删除元素,得到区间[l,r)后摘除子树。

实现:刚开始看时还纳闷为什么在实现时都要先加根节点以及根节点的右孩子,然后把整个区间建在根的右孩子的左子树上。后来才想到对于整个区间[1,N+1),这种做法相当于在开始和结尾各添加一个元素,对整个区间进行操作则需要把这两个结点splay到树根和根的右孩子,才能得到整个区间。在第一个元素前和最后一个元素后插入都需要借助这两个结点。

ref: https://zhuanlan.zhihu.com/p/32090049?iam=5a347fde94eade0536f38c5e7434e822

http://blog.csdn.net/acm_cxlove/article/details/7800979

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
HDU 3487 Play With Chain

#include <iostream>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <algorithm>
#include <functional>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>

using namespace std;

#define MAX_N 300010
#define rrclc ch[ch[root][1]][0]

int tot, root;
int size[MAX_N], val[MAX_N], par[MAX_N], ch[MAX_N][2], rev[MAX_N];

int N, M;
int nums[MAX_N];

// update x from its two children
void push_up(int x) {
size[x] = size[ch[x][0]] + size[ch[x][1]] + 1;
}

// make the lazy tag down
void push_down(int x) {
if (rev[x]) {
swap(ch[x][0], ch[x][1]);
rev[ch[x][0]] ^= 1;
rev[ch[x][1]] ^= 1;
rev[x] ^= 1;
}
}

// x will be changed to the pos of new node
// v val, p par
void new_node(int &x, int v, int p) {
x = ++tot;
size[x] = 1;
val[x] = v;
par[x] = p;
ch[x][0] = ch[x][1] = 0;
rev[x] = 0;
}

// x points to new node
// interval [l, r), par p
void build(int &x, int l, int r, int p) {
if (l >= r) return;
int m = (l + r) / 2;
new_node(x, nums[m], p);
build(ch[x][0], l, m, x);
build(ch[x][1], m + 1, r, x);
push_up(x);
}

//
void init() {
tot = root = 0;
size[0] = val[0] = par[0] = ch[0][0] = ch[0][1] = rev[0] = 0;
new_node(root, 0, 0);
new_node(ch[root][1], 0, root);
build(rrclc, 1, N + 1, ch[root][1]);
push_up(ch[root][1]);
push_up(root);
}

// level x up through rotation
void rotate(int x) {
int p = par[x], g = par[p];
push_down(p); push_down(x);
int tx = ch[p][0] == x, tp = ch[g][0] == p;

ch[p][!tx] = ch[x][tx];
if (ch[x][tx]) par[ch[x][tx]] = p;

ch[x][tx] = p; par[p] = x;

par[x] = g;
if (g) ch[g][!tp] = x;

push_up(p); push_up(x);
}

// splay node x up until its par is r
void splay(int x, int r) {
int p = par[x];
while (p != r) {
int g = par[p];
if (g == r) rotate(x); // two level
else { // three level
push_down(g); push_down(p);
int tx = ch[p][0] == x, tp = ch[g][0] == p;
if (tx == tp) rotate(p);
else rotate(x);
rotate(x);
}
p = par[x];
}
if (r == 0) root = x;
}

// r is the root of subtree
// k: the order of inorder tranverse
int get_kth(int r, int k) {
push_down(r);
int ls = size[ch[r][0]], rs = size[ch[r][1]];
if (ls == k) return r;
else if (k < ls) return get_kth(ch[r][0], k);
else return get_kth(ch[r][1], k - ls - 1);
}

void cut(int l, int r, int c) {
splay(get_kth(root, l - 1), 0);
splay(get_kth(root, r + 1), root);

int cr = rrclc;
par[cr] = 0; rrclc = 0;
push_up(ch[root][1]); push_up(root);

splay(get_kth(root, c), 0);
splay(get_kth(root, c + 1), root);

rrclc = cr; par[cr] = ch[root][1];
push_up(ch[root][1]); push_up(root);
}

void flip(int l, int r) {
splay(get_kth(root, l - 1), 0);
splay(get_kth(root, r + 1), root);
rev[rrclc] ^= 1;
}

void level(int r) {
if (!r) return;
queue<int> q;
q.push(r);
while (!q.empty()) {
int s = q.size();
while (s--) {
int x = q.front(); q.pop();
if (ch[x][0]) q.push(ch[x][0]);
if (ch[x][1]) q.push(ch[x][1]);
printf("%d ", val[x]);
}
printf("\n");
}
}

bool first;
void inorder(int r) {
if (!r) return;
push_down(r);
inorder(ch[r][0]);
if (first) {
first = !first;
printf("%d", val[r]);
} else {
printf(" %d", val[r]);
}
inorder(ch[r][1]);
}

int main() {
while (scanf("%d %d", &N, &M) && N > 0 && M > 0) {
for (int i = 1; i <= N; i++) nums[i] = i;
init();
while (M--) {
char cmd[10];
scanf("%s", cmd);
if (cmd[0] == 'C') {
int a, b, c; scanf("%d %d %d", &a, &b, &c);
cut(a, b, c);
} else {
int a, b; scanf("%d %d", &a, &b);
flip(a, b);
}
}
splay(get_kth(root, 0), 0);
splay(get_kth(root, N + 1), root);

first = true;
inorder(rrclc);
printf("\n");
}
// N = 10;
// for (int i = 1; i <= N; i++) nums[i] = i;
// init();
// splay(get_kth(root, 1), 0);
// splay(get_kth(root, 5), root);
// level(root);
// inorder(root);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
POJ 3580 SuperMemo

#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <algorithm>
#include <functional>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>

using namespace std;
#define MAX_N 200005
#define rrclc ch[ch[root][1]][0]

const int INF = 1000000000;

int tot, root;
int size[MAX_N], par[MAX_N], ch[MAX_N][2], val[MAX_N];
int rev[MAX_N], add[MAX_N], mi[MAX_N];

int N, M;
int nums[MAX_N];

void inorder(int r);
void level(int r);

void push_up(int x) {
int lc = ch[x][0], rc = ch[x][1];
size[x] = size[lc] + size[rc] + 1;
mi[x] = min(val[x], min(mi[lc], mi[rc]));
}

void push_down(int x) {
if (rev[x]) {
swap(ch[x][0], ch[x][1]);
if (ch[x][0]) rev[ch[x][0]] ^= 1;
if (ch[x][1]) rev[ch[x][1]] ^= 1;
rev[x] ^= 1;
}
if (add[x] != 0) {
for (int i = 0; i < 2; i++) {
if (ch[x][i]) {
add[ch[x][i]] += add[x];
val[ch[x][i]] += add[x];
mi[ch[x][i]] += add[x];
}
}
add[x] = 0;
}
}

void new_node(int &x, int v, int p) {
x = ++tot;
size[x] = 1;
par[x] = p;
ch[x][0] = ch[x][1] = 0;
val[x] = v;
rev[x] = 0;
add[x] = 0;
mi[x] = val[x];
}

void del_node(int x) {
if (!x) return;
if (ch[x][0]) del_node(ch[x][0]);
if (ch[x][1]) del_node(ch[x][1]);
// tot--;
}

void build(int &x, int l, int r, int p) {
if (l >= r) return;
int m = (l + r) / 2;
new_node(x, nums[m], p);
build(ch[x][0], l, m, x);
build(ch[x][1], m + 1, r, x);
push_up(x);
}

void init() {
tot = -1;
new_node(root, 0, 0);
size[root] = 0; mi[root] = INF;
new_node(root, 0, 0);
new_node(ch[root][1], 0, root);
build(rrclc, 1, N + 1, ch[root][1]);
push_up(ch[root][1]);
push_up(root);
}

void rotate(int x) {
int p = par[x], g = par[p]; // if (p == 0) ?
push_down(p); push_down(x);
int tx = ch[p][0] == x, tp = ch[g][0] == p;

ch[p][!tx] = ch[x][tx];
if (ch[x][tx]) par[ch[x][tx]] = p;

ch[x][tx] = p; par[p] = x;

par[x] = g;
if (g) ch[g][!tp] = x;

push_up(p); push_up(x);
}

void splay(int x, int r) {
int p = par[x];
while (p != r) {
int g = par[p];
if (g == r) rotate(x);
else {
push_down(g); push_down(p);
int tx = ch[p][0] == x, tp = ch[g][0] == p;
if (tx == tp) rotate(p);
else rotate(x);
rotate(x);
}
p = par[x];
}
if (r == 0) root = x;
}

int get_kth(int r, int k) {
push_down(r);
int ls = size[ch[r][0]], rs = size[ch[r][1]];
if (ls == k) return r;
else if (k < ls) return get_kth(ch[r][0], k);
else return get_kth(ch[r][1], k - ls - 1);
}

// insert new node with val v after k
void insert(int k, int v) {
splay(get_kth(root, k), 0);
splay(get_kth(root, k + 1), root);
int x;
new_node(x, v, ch[root][1]);
rrclc = x;
push_up(ch[root][1]);
push_up(root);
}

// remove [l, r) from splay tree
int remove(int l, int r) {
if (l >= r) return 0;
splay(get_kth(root, l - 1), 0);
splay(get_kth(root, r), root);
int cr = rrclc;
rrclc = 0; par[cr] = 0;
push_up(ch[root][1]);
push_up(root);
return cr;
}

void lazy_add(int l, int r, int d) {
splay(get_kth(root, l - 1), 0);
splay(get_kth(root, r), root);
add[rrclc] += d;
mi[rrclc] += d;
val[rrclc] += d;
}

void reverse(int l, int r) {
splay(get_kth(root, l - 1), 0);
splay(get_kth(root, r), root);
rev[rrclc] ^= 1;
}

void revolve(int l, int r, int t) {
int m = r - l;
t %= m;
if (t == 0) return;
if (t < 0) t += m;
int c1 = remove(r - t, r - 1), c2 = remove(l, r - t);
// (r - 1) => (l)
splay(get_kth(root, l - 1), 0);
splay(get_kth(root, l + 1), root);
ch[rrclc][0] = c1;
if (c1) par[c1] = rrclc;
ch[rrclc][1] = c2;
if (c2) par[c2] = rrclc;

push_up(rrclc); push_up(ch[root][1]); push_up(root);
// reverse(l, r - t);
// reverse(r - t, r);
// reverse(l, r);
}

int get_min(int l, int r) {
splay(get_kth(root, l - 1), 0);
splay(get_kth(root, r), root);
push_down(rrclc);
return mi[rrclc];
}

void level(int r) {
if (!r) return;
queue<int> q;
q.push(r);
while (!q.empty()) {
int s = q.size();
while (s--) {
int f = q.front(); q.pop();
if (ch[f][0]) q.push(ch[f][0]);
if (ch[f][1]) q.push(ch[f][1]);
printf("%d ", val[f]);
}
printf("\n");
}
}

void inorder(int r) {
if (!r) return;
push_down(r);
inorder(ch[r][0]);
printf("%d ", val[r]);
inorder(ch[r][1]);
}

int main() {
// freopen("in.txt", "r", stdin);
scanf("%d", &N);
for (int i = 1; i <= N; i++) scanf("%d", nums + i);
init();
scanf("%d", &M);
while (M--) {
char cmd[10];
scanf("%s", cmd);
int a, b, c;
if (cmd[0] == 'A') {
scanf("%d %d %d", &a, &b, &c);
lazy_add(a, b + 1, c);
} else if (cmd[0] == 'R' && cmd[3] == 'E') {
scanf("%d %d", &a, &b);
reverse(a, b + 1);
} else if (cmd[0] == 'R' && cmd[3] == 'O') {
scanf("%d %d %d", &a, &b, &c);
revolve(a, b + 1, c);
} else if (cmd[0] == 'I') {
scanf("%d %d", &a, &b);
insert(a, b);
} else if (cmd[0] == 'D') {
scanf("%d %d", &a);
remove(a, a + 1);
} else {
scanf("%d %d", &a, &b);
// inorder(root); printf("\n");
printf("%d\n", get_min(a, b + 1));
}
}
return 0;
}

Treap:BST+heap,每个结点维护两个属性key,priority,结点的key满足bst,left.key<x.key<right.key;priority满足heap的性质,x.priority<child.priority(若min heap)。相当于是首先把所有结点按照priority的大小从小到大排序,然后按照排序后的顺序依次插入bst。结点的priority是随机的,整个按照priority排序的序列相当于一个随机的序列,因n个关键字随机插入所构建的bst的高度期望是O(logn),treap的高度的期望也是O(logn)。

一般treap可用来维护一个序列,给定x,查询x在序列排序后的位置;或给定位置k,查询元素x

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
POJ 1442 Black Box

#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <map>
#include <algorithm>
#include <functional>
#include <stack>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>

using namespace std;

const int MAX_M = 30000 + 10;
const int MAX_N = 30000 + 10;
const int INF = ~0U >> 1;

int M, N;
int A[MAX_M], u[MAX_N];

struct Node {
Node *ch[2], *par;
int key, prio, size;
Node() {
size = 0;
}
void set_ch(Node* c, bool tc) {
ch[tc] = c;
c->par = this;
}
bool tn() { return par->ch[1] == this; }
void up() {
size = ch[0]->size + ch[1]->size + 1;
}

} Tnull, *null = &Tnull;
// null's par and children may be modified

Node mem[MAX_M], *ct = mem;

Node* root;

void init() {
root = null;
}

Node* new_node(int k) {
ct->key = k;
ct->prio = rand();
ct->size = 1;
ct->ch[0] = ct->ch[1] = null;
ct->par = null;
return ct++;
}

void rotate(Node* x) {
Node *p = x->par, *g = p->par; // p != null
bool tx = x->tn(), tp = p->tn();
// p->set_ch(x->ch[!tx], tx);
p->ch[tx] = x->ch[!tx];
if (x->ch[!tx] != null) x->ch[!tx]->par = p;
x->set_ch(p, !tx);
p->up();
x->up();
x->par = g;
if (g != null) g->ch[tp] = x;
else root = x;
}

// min heap
// x's left child.key < x.key
// right child.key >= x.key
void insert(int k) {
Node* x = new_node(k);
if (root == null) {
root = x;
return;
}
Node* p = root;
while (p != null) {
if (k < p->key) {
if (p->ch[0] == null) {
p->set_ch(x, 0);
break;
}
p = p->ch[0];
} else {
if (p->ch[1] == null) {
p->set_ch(x, 1);
break;
}
p = p->ch[1];
}
}
while (p != null) {
p->up();
p = p->par;
}
while (x->par != null && x->par->prio > x->prio) {
rotate(x);
}
}

Node* get_kth(int k) {
Node* p = root;
while (p != null) {
int ls = p->ch[0]->size;
if (ls == k) return p;
else if (k < ls) p = p->ch[0];
else {
k = k - ls - 1;
p = p->ch[1];
}
}
return null;
}

void inorder(Node* r) {
if (r == null) return;
inorder(r->ch[0]);
printf("%d ", r->key);
inorder(r->ch[1]);
}

int main() {
// freopen("in.txt", "r", stdin);
scanf("%d %d", &M, &N);
init();
for (int i = 1; i <= M; i++) scanf("%d", A + i);
// for (int i = 1; i <= M; i++) {
// insert(A[i]);
// inorder(root); printf("\n");
// }
for (int i = 1; i <= N; i++) scanf("%d", u + i);
u[0] = 0;
for (int i = 1; i <= N; i++) {
for (int j = u[i - 1] + 1; j <= u[i]; j++) {
insert(A[j]);
}
// inorder(root); printf("\n");
printf("%d\n", get_kth(i - 1)->key);
}
return 0;
}

AVL Tree:nice的结构,在插入删除时进行旋转操作保证左右树高差不超过1

旋转操作比较复杂,总结一下即左高时右旋,若左子树右高,则左子树先左旋;右高时左旋,若右子树左高,则右子树先右旋。

AVL树与Treap一样都是用来实现BBST,还有红黑树,不过实现就有点复杂了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
SPOJ AVL Tree

#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <map>
#include <algorithm>
#include <functional>
#include <stack>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>

using namespace std;

const int MAX_N = 200000 + 10;

class AVLTree {
public:
struct Node {
int key, val, size, height;
Node* ch[2]; // no par pointer => all rec
Node() : size(0), height(0) {}
void up() {
size = ch[0]->size + ch[1]->size + 1;
height = max(ch[0]->height, ch[1]->height) + 1;
}
int bf() { // left.height - right.height
return ch[0]->height - ch[1]->height;
}
} Tnull, *null;

Node mem[MAX_N], *ct;

Node* new_node(int key, int val) {
ct->key = key;
ct->val = val;
ct->size = 1;
ct->height = 1;
ct->ch[0] = ct->ch[1] = null;
return ct++;
}

Node* root;

AVLTree() {
null = &Tnull;
ct = mem;
root = null;
}

void insert(int key, int val) {
if (root == null) {
root = new_node(key, val);
return;
}
put(root, key, val);
}

Node* put(Node* x, int key, int val) {
if (x == null) return new_node(key, val);
if (key < x->key) {
x->ch[0] = put(x->ch[0], key, val);
} else if (x->key < key) {
x->ch[1] = put(x->ch[1], key, val);
} else {
x->val = val;
return x;
}
x->up();
return balance(x);
}

// t: 1 right rotation; 0 left rotation
Node* rotate(Node* x, int t) {
Node* c = x->ch[!t];
x->ch[!t] = c->ch[t];
c->ch[t] = x;
x->up();
c->up();
if (root == x) root = c;
return c;
}

Node* balance(Node* x) {
if (x->bf() < -1) { // x right higher
if (x->ch[1]->bf() > 0) {
x->ch[1] = rotate(x->ch[1], 1);
}
x = rotate(x, 0);
} else if (x->bf() > 1) { // x left higher
if (x->ch[0]->bf() < 0) {
x->ch[0] = rotate(x->ch[0], 0);
}
x = rotate(x, 1);
}
return x;
}

void remove() {

}

int rank(Node* x, int key) {
if (x == null) return -1;
if (x->key == key) return x->ch[0]->size;
else if (key < x->key) {
return rank(x->ch[0], key);
} else {
int r = rank(x->ch[1], key);
return (r == -1) ? r : x->ch[0]->size + 1 + r;
}
}

void inorder(Node* x) {
if (x == null) return;
inorder(x->ch[0]);
printf("%d ", x->key);
inorder(x->ch[1]);
}
};

int main() {
// freopen("in.txt", "r", stdin);
AVLTree* t = new AVLTree();
int Q, type, n, r;
scanf("%d", &Q);
while (Q--) {
scanf("%d %d", &type, &n);
if (type == 1) {
t->insert(n, 0);
// t->inorder(t->root); printf("\n");
} else {
r = t->rank(t->root, n);
if (r == -1) {
printf("Data tidak ada\n");
} else {
printf("%d\n", r + 1);
}
}
}
return 0;
}