forked from EndlessCheng/codeforces-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
treap.go
248 lines (222 loc) · 6.44 KB
/
treap.go
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
package copypasta
import (
. "fmt"
"strings"
"time"
)
/* 树堆 treap=tree+heap
本质上属于笛卡尔树,见 cartesian_tree.go
https://oi-wiki.org/ds/treap/
https://en.wikipedia.org/wiki/Treap
复杂度证明 http://www.cs.cmu.edu/afs/cs/academic/class/15210-s12/www/lectures/lecture16.pdf
部分代码参考刘汝佳实现,见 https://github.com/klb3713/aoapc-book/blob/master/TrainingGuide/bookcodes/ch3/la5031.cpp
额外维护子树和的写法见 https://codeforces.com/contest/1398/submission/119651187
todo Merging treaps https://codeforces.com/blog/entry/108601
模板题 https://www.luogu.com.cn/problem/P3369 https://www.luogu.com.cn/problem/P6136
题目推荐 https://cp-algorithms.com/data_structures/treap.html#toc-tgt-8
https://codeforces.com/problemset/problem/85/D 较为复杂的维护
https://atcoder.jp/contests/abc245/tasks/abc245_e 离线+lowerbound+delete
https://atcoder.jp/contests/abc356/tasks/abc356_f 维护关键位置
*/
// 用 GoLand 的话强烈建议加入到 Live Templates 中,比赛时直接敲快捷键
type tpKeyType int
type tpValueType int
type tpNode struct {
lr [2]*tpNode
priority uint // max heap
sz int
msz int
key tpKeyType
val tpValueType // dupCnt for multiset
}
// 设置如下返回值是为了方便使用 tpNode 中的 lr 数组
func (o *tpNode) cmp(a tpKeyType) int {
b := o.key
if a == b {
return -1
}
if a < b {
return 0 // 左儿子
}
return 1 // 右儿子
}
func (o *tpNode) size() int {
if o != nil {
return o.sz
}
return 0
}
func (o *tpNode) mSize() int {
if o != nil {
return o.msz
}
return 0
}
// 对于取名叫 maintain 还是 pushUp,由于操作的对象是当前节点,个人认为取名 maintain 更为准确
func (o *tpNode) maintain() {
o.sz = 1 + o.lr[0].size() + o.lr[1].size()
o.msz = int(o.val) + o.lr[0].mSize() + o.lr[1].mSize()
}
// 旋转,并维护子树大小
// d=0:左旋,返回 o 的右儿子
// d=1:右旋,返回 o 的左儿子
func (o *tpNode) rotate(d int) *tpNode {
x := o.lr[d^1]
o.lr[d^1] = x.lr[d]
x.lr[d] = o
// x.sz = o.sz; x.msz = o.msz; o.maintain()
o.maintain()
x.maintain()
return x
}
type treap struct {
rd uint
root *tpNode
}
// 也可以直接设 rd 为 1
func newTreap() *treap {
return &treap{rd: uint(time.Now().UnixNano())/2 + 1}
}
// https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf
// https://en.wikipedia.org/wiki/Xorshift
// 任何 Go 版本都通用的写法
func (t *treap) fastRand() uint {
t.rd ^= t.rd << 13
t.rd ^= t.rd >> 17
t.rd ^= t.rd << 5
return t.rd
}
// 插入一键值对,返回插入后优先级最大的节点
// 先和二叉搜索树的插入一样,先把要插入的点插入到一个叶子上,并随机分配一个优先级,
// 然后跟维护堆一样,如果当前节点的优先级比根大就旋转,如果当前节点是根的左儿子就右旋如果当前节点是根的右儿子就左旋
func (t *treap) _put(o *tpNode, key tpKeyType, val tpValueType) *tpNode {
if o == nil {
return &tpNode{priority: t.fastRand(), sz: 1, msz: 1, key: key, val: val}
}
if d := o.cmp(key); d >= 0 {
o.lr[d] = t._put(o.lr[d], key, val)
// 优先级比根大就旋转
if o.lr[d].priority > o.priority {
// 是根的左儿子就右旋,反之左旋
o = o.rotate(d ^ 1)
}
} else {
//o.val = val
o.val += val
}
o.maintain()
return o
}
func (t *treap) put(key tpKeyType, val tpValueType) { t.root = t._put(t.root, key, val) }
// 删除一个键,返回删除后优先级最大的节点,若无节点返回 nil
// 因为 treap 满足堆性质,所以只需要把要删除的节点旋转到叶节点上,然后直接删除就可以了
// 具体的方法就是每次找到优先级最大的儿子,向与其相反的方向旋转,这样要删除的节点会不断下降直到叶节点,然后直接删除
func (t *treap) _delete(o *tpNode, key tpKeyType) *tpNode {
if o == nil {
return nil
}
if d := o.cmp(key); d >= 0 {
o.lr[d] = t._delete(o.lr[d], key)
} else {
if o.val > 1 {
o.val--
} else {
if o.lr[1] == nil {
return o.lr[0]
}
if o.lr[0] == nil {
return o.lr[1]
}
// o 有两棵子树,把优先级高的子树旋转到根,然后递归在另一棵子树中删除 o
d = 0
if o.lr[0].priority > o.lr[1].priority {
d = 1
}
o = o.rotate(d)
o.lr[d] = t._delete(o.lr[d], key)
}
}
o.maintain()
return o
}
func (t *treap) delete(key tpKeyType) { t.root = t._delete(t.root, key) }
// 其余通用方法见 bst.go
//
func (o *tpNode) String() (s string) {
//return strconv.Itoa(int(o.key))
if o.val == 1 {
s = Sprintf("%v", o.key)
} else {
s = Sprintf("%v(%v)", o.key, o.val)
}
s += Sprintf("[sz:%d,msz:%d]", o.sz, o.msz)
return
}
/* 逆时针旋转 90° 打印这棵树:根节点在最左侧,右子树在上侧,左子树在下侧
效果如下(只打印 key)
Root
│ ┌── 95
│ ┌── 94
│ ┌── 90
│ │ │ ┌── 89
│ │ │ ┌── 88
│ │ │ │ └── 87
│ │ │ │ └── 81
│ │ │ ┌── 74
│ │ └── 66
└── 62
│ ┌── 59
│ ┌── 58
│ │ └── 56
│ │ └── 47
│ ┌── 45
└── 40
│ ┌── 37
│ ┌── 28
└── 25
│ ┌── 18
│ ┌── 15
│ ┌── 11
└── 6
└── 0
*/
func (o *tpNode) draw(treeSB, prefixSB *strings.Builder, isTail bool) {
prefix := prefixSB.String()
if o.lr[1] != nil {
newPrefixSB := &strings.Builder{}
newPrefixSB.WriteString(prefix)
if isTail {
newPrefixSB.WriteString("│ ")
} else {
newPrefixSB.WriteString(" ")
}
o.lr[1].draw(treeSB, newPrefixSB, false)
}
treeSB.WriteString(prefix)
if isTail {
treeSB.WriteString("└── ")
} else {
treeSB.WriteString("┌── ")
}
treeSB.WriteString(o.String())
treeSB.WriteByte('\n')
if o.lr[0] != nil {
newPrefixSB := &strings.Builder{}
newPrefixSB.WriteString(prefix)
if isTail {
newPrefixSB.WriteString(" ")
} else {
newPrefixSB.WriteString("│ ")
}
o.lr[0].draw(treeSB, newPrefixSB, true)
}
}
func (t *treap) String() string {
if t.root == nil {
return "Empty\n"
}
treeSB := &strings.Builder{}
treeSB.WriteString("Root\n")
t.root.draw(treeSB, &strings.Builder{}, true)
return treeSB.String()
}