Lançado · em melhoria
Guia de Árvores de segmentos · 6/6
Por enquanto, este capítulo está disponível apenas em inglês.
Segment trees and Fenwick trees are staples of coding interviews, but they also show up quietly in real systems wherever a changing sequence needs range summaries. This chapter covers where they appear, libraries you can use directly, and the most common mistakes.
O(log n). When a score changes, subtract from the old slot and add to the new one.class Leaderboard:
"""Scores are integers in 0..max_score. Rank = players with a higher score + 1."""
def __init__(self, max_score):
self.size = max_score + 1
self.tree = [0] * (self.size + 1)
self.scores = {}
def _add(self, score, delta):
i = score + 1
while i <= self.size:
self.tree[i] += delta
i += i & -i
def _count_at_most(self, score):
i, total = score + 1, 0
while i > 0:
total += self.tree[i]
i -= i & -i
return total
def set_score(self, player, score):
if player in self.scores:
self._add(self.scores[player], -1)
self.scores[player] = score
self._add(score, 1)
def rank(self, player):
higher = len(self.scores) - self._count_at_most(self.scores[player])
return higher + 1
board = Leaderboard(1000)
for name, s in [("ana", 720), ("ben", 950), ("cho", 720), ("dev", 400)]:
board.set_score(name, s)
print(board.rank("ana"), board.rank("ben"), board.rank("dev")) # 2 1 4
board.set_score("dev", 990)
print(board.rank("dev"), board.rank("ben")) # 1 2Instead of writing your own, you can use a tested implementation. The AtCoder Library (ACL) provides segtree, lazy_segtree, and fenwick_tree for C++. You pass the operation and the identity as functions, which maps directly onto the monoid idea from the first chapter.
#include <atcoder/segtree>
#include <climits>
#include <iostream>
#include <vector>
int op(int a, int b) { return a < b ? a : b; } // minimum
int e() { return INT_MAX; } // identity
int main() {
std::vector<int> v = {5, 3, 7, 9, 6, 4, 1, 2};
atcoder::segtree<int, op, e> seg(v);
std::cout << seg.prod(2, 6) << '\n'; // minimum of [2, 6): 4
seg.set(4, 0);
std::cout << seg.prod(2, 6) << '\n'; // 0
std::cout << seg.all_prod() << '\n'; // 0
}The Python standard library has no segment tree, so people usually copy the short iterative implementation from the implementation chapter. If the values never change, prefix sums built with itertools.accumulate are enough.
[l, r] and half-open [l, r) ranges causes off-by-one errors. Pick one and note it in each function's docstring.2n slots overflows when n is not a power of two. Allocate 4n.int overflow quickly in C++ and Java. Use long long or long.push loses earlier range updates.i & -i is 0 when i = 0, so a 0-based loop never terminates. Use 1-based indices internally.INF = float("inf")
values = [7, 3, 9]
def range_min(values, identity):
best = identity
for v in values:
best = min(best, v)
return best
print(range_min(values, 0)) # 0 (wrong: the identity swallowed the answer)
print(range_min(values, INF)) # 3 (correct)Segment trees and Fenwick trees summarize changing sequences by range in frequency tables, leaderboards, metric aggregation, sweep-line geometry, and booking systems. Watch out for five things, range conventions, identities, array size, integer overflow, and missing pushes, and most bugs disappear.
0 comentários
Fazer login · Faça login para deixar um comentário.
Seja o primeiro a comentar.