Informacje i teoria
Drzewo przedziałowe to drzewo binarne zbudowane nad indeksami tablicy, w którym każdy węzeł pokrywa spójny przedział. Liście pokrywają pojedyncze elementy; każdy węzeł wewnętrzny przechowuje agregat (sumę lub minimum) swojego przedziału, obliczony z dwóch jego węzłów potomnych.
Budowa w czasie O(n)
Budowa od dołu do góry (lub przez rekurencyjny podział
metodą „dziel i zwyciężaj”) łączy n liści w
~2n węzłów łącznie, wykonując O(1)
pracy na węzeł — czyli O(n) łącznie.
Zapytanie na przedziale w czasie O(log n)
Zapytanie [L,R] rekurencyjnie schodzi w
przedział węzła: jeśli leży on całkowicie poza
[L,R] — pomijamy; jeśli leży całkowicie
wewnątrz — używamy bezpośrednio wcześniej obliczonego
agregatu węzła; jeśli częściowo się pokrywa — schodzimy
rekurencyjnie do obu węzłów potomnych. Łącznie łączy się
co najwyżej O(log n) węzłów, ponieważ
przedział zapytania rozkłada się co najwyżej na dwa
„łańcuszki” kanonicznych podprzedziałów na każdym poziomie
drzewa.
Aktualizacja punktowa w czasie O(log n)
Aktualizacja jednego elementu tablicy unieważnia jedynie
O(log n) przodków na ścieżce od tego liścia do
korzenia; każdy z nich jest przeliczany na podstawie
dwóch swoich węzłów potomnych w drodze powrotnej.
Dlaczego nie po prostu sumy prefiksowe?
Sumy prefiksowe odpowiadają na zapytanie o sumę na
przedziale w czasie O(1), ale wymagają
O(n) do aktualizacji jednego elementu i w
ogóle nie obsługują zapytania o minimum na przedziale
(minimum nie ma operacji odwrotnej). Drzewo przedziałowe
zamienia niewielką część szybkości zapytania na szybkie
aktualizacje i obsługę dowolnego operatora łącznego.
Poza tą symulacją
Leniwa propagacja rozszerza drzewa przedziałowe,
aby obsługiwać aktualizacje w czasie O(log n)
na całym przedziale (a nie tylko punktowe
aktualizacje), odkładając niezastosowane aktualizacje
poddrzew, dopóki naprawdę nie zostaną odwiedzone.