-
Notifications
You must be signed in to change notification settings - Fork 34
Expand file tree
/
Copy pathintmap.go
More file actions
236 lines (205 loc) · 5.65 KB
/
intmap.go
File metadata and controls
236 lines (205 loc) · 5.65 KB
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
// Package fastinteger is designed to provide a very primitive
// implementation of a hash map for unsigned integer keys and
// values. It is designed to have existence checks and insertions
// that are faster than Go's native implementation. Like Go's
// native implementation, FastIntegerHashMap will dynamically
// grow in size.
//
// Current benchmarks on identical machine against native Go implementation:
//
// BenchmarkInsert-8 10000 131258 ns/op
// BenchmarkGoMapInsert-8 10000 208787 ns/op
// BenchmarkExists-8 100000 15820 ns/op
// BenchmarkGoMapExists-8 100000 16394 ns/op
// BenchmarkDelete-8 100000 17909 ns/op
// BenchmarkGoDelete-8 30000 49376 ns/op
// BenchmarkInsertWithExpand-8 20000 90301 ns/op
// BenchmarkGoInsertWithExpand-8 10000 142088 ns/op
//
// This performance could be further enhanced by using a
// better probing technique.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
yosupo()
}
// https://judge.yosupo.jp/problem/associative_array
func yosupo() {
in := bufio.NewReader(os.Stdin)
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
var q int
fmt.Fscan(in, &q)
mp := NewIntMap(2e6)
for i := 0; i < q; i++ {
var op int
fmt.Fscan(in, &op)
if op == 0 {
var pos, val uint64
fmt.Fscan(in, &pos, &val)
mp.Set(pos, val)
} else if op == 1 {
var pos uint64
fmt.Fscan(in, &pos)
res, ok := mp.Get(pos)
if ok {
fmt.Fprintln(out, res)
} else {
fmt.Fprintln(out, 0)
}
}
}
}
const ratio = .75 // ratio sets the capacity the hashmap has to be at before it expands
// roundUp takes a uint64 greater than 0 and rounds it up to the next
// power of 2.
func roundUp(v uint64) uint64 {
v--
v |= v >> 1
v |= v >> 2
v |= v >> 4
v |= v >> 8
v |= v >> 16
v |= v >> 32
v++
return v
}
type packet struct {
key, value uint64
}
type packets []*packet
func (packets packets) find(key uint64) uint64 {
h := hash(key)
i := h & (uint64(len(packets)) - 1)
for packets[i] != nil && packets[i].key != key {
i = (i + 1) & (uint64(len(packets)) - 1)
}
return i
}
func (packets packets) set(packet *packet) {
i := packets.find(packet.key)
if packets[i] == nil {
packets[i] = packet
return
}
packets[i].value = packet.value
}
func (packets packets) get(key uint64) (uint64, bool) {
i := packets.find(key)
if packets[i] == nil {
return 0, false
}
return packets[i].value, true
}
func (packets packets) delete(key uint64) bool {
i := packets.find(key)
if packets[i] == nil {
return false
}
packets[i] = nil
i = (i + 1) & (uint64(len(packets)) - 1)
for packets[i] != nil {
p := packets[i]
packets[i] = nil
packets.set(p)
i = (i + 1) & (uint64(len(packets)) - 1)
}
return true
}
func (packets packets) has(key uint64) bool {
i := packets.find(key)
return packets[i] != nil // technically, they can store nil
}
// FastIntegerHashMap is a simple hashmap to be used with
// integer only keys. It supports few operations, and is designed
// primarily for cases where the consumer needs a very simple
// datastructure to set and check for existence of integer
// keys over a sparse range.
type FastIntegerHashMap struct {
count uint64
packets packets
}
// rebuild is an expensive operation which requires us to iterate
// over the current bucket and rehash the keys for insertion into
// the new bucket. The new bucket is twice as large as the old
// bucket by default.
func (fi *FastIntegerHashMap) rebuild() {
packets := make(packets, roundUp(uint64(len(fi.packets))+1))
for _, packet := range fi.packets {
if packet == nil {
continue
}
packets.set(packet)
}
fi.packets = packets
}
// Get returns an item from the map if it exists. Otherwise,
// returns false for the second argument.
func (fi *FastIntegerHashMap) Get(key uint64) (uint64, bool) {
return fi.packets.get(key)
}
// Set will set the provided key with the provided value.
func (fi *FastIntegerHashMap) Set(key, value uint64) {
if float64(fi.count+1)/float64(len(fi.packets)) > ratio {
fi.rebuild()
}
fi.packets.set(&packet{key: key, value: value})
fi.count++
}
// Has will return a bool indicating if the provided key
// exists in the map.
func (fi *FastIntegerHashMap) Has(key uint64) bool {
return fi.packets.has(key)
}
// Delete will remove the provided key from the hashmap. If
// the key cannot be found, this is a no-op.
func (fi *FastIntegerHashMap) Delete(key uint64) {
if fi.packets.delete(key) {
fi.count--
}
}
// Len returns the number of items in the hashmap.
func (fi *FastIntegerHashMap) Len() uint64 {
return fi.count
}
// Cap returns the capacity of the hashmap.
func (fi *FastIntegerHashMap) Cap() uint64 {
return uint64(len(fi.packets))
}
func (fi *FastIntegerHashMap) ForEach(f func(key, value uint64) (stop bool)) {
for _, packet := range fi.packets {
if packet == nil {
continue
}
if f(packet.key, packet.value) {
break
}
}
}
// NewIntMap returns a new FastIntegerHashMap with a bucket size specified
// by hint.
func NewIntMap(hint uint64) *FastIntegerHashMap {
if hint == 0 {
hint = 16
}
hint = roundUp(hint)
return &FastIntegerHashMap{
count: 0,
packets: make(packets, hint),
}
}
// hash will convert the uint64 key into a hash based on Murmur3's 64-bit
// integer finalizer.
// Details here: https://code.google.com/p/smhasher/wiki/MurmurHash3
func hash(key uint64) uint64 {
key ^= key >> 33
key *= 0xff51afd7ed558ccd
key ^= key >> 33
key *= 0xc4ceb9fe1a85ec53
key ^= key >> 33
return key
}