Riedky intervalový strom

Občas je postupnosť, na ktorej chceme robiť zmeny a pýtať sa na otázky príliš veľká na to, aby sme na nej vytvorili celý intervalový strom. Chceme ale stále vedieť nejakým spôsobom zmeniť prvky jej prvky na nejkom intervale (napríklad vynásobiť konštantou) a tiež získať výsledok operácie na intervale (napríklad súčet prvkov). Takto veľké postupnosti zvyčajne nie su zadané explicitne. Väčšinou na začiatku vieme, že všetky jej prvky sú nastavené na nejakú hodnotu (v našom prípade dáva najväčší zmysel hodnota $1$). My si ukážeme, ako aj na takto veľkej postupnosti robiť operácie, ktoré sme vededli robiť na obyčajnom intrevalovom strome s lazy propagation.

Hlavná myšlienka bude podobná, ako pri spomínanej lazy propagation. Akurát teraz vrcholy, ktoré nepotrebujeme pri pýtaní sa na nejaký interval, alebo pri menení prvkov na nejakom intervale, nebudeme ani vytvárať. Intervalový strom sa bude vytvárať „za behu“. Začneme iba s koreňom, ktorý bude reprezentovať súčet celého intervalu zo začiatku. Ďalšie vrcholy budeme vytvárať podľa potreby pri pýtaní sa na intervaly, alebo pri menení prvkov. Jediným rozdielom oproti predchádzajúcej implementácii intervalových operácii bude, že predtým než v týchto operáciach pristúpime k synom aktuálneho vrchola skontrolujeme, či existujú a ak nie, tak ich vytvoríme s vhodnou hodntou.

Všimnime si, že vrcholy sme stále vopred v poli vytvárali iba kvôli tomu, aby sme poznali ich počiatočnú hodnotu a mali ich uložené na správnom indexe v poli (vďaka čomu sme vedeli zistiť synov ľubovoľného vrcholu). V našom prípade, keď intervalový strom vytvárame nad poľom samých jednotiek, vieme počiatočnú hodnotu vrcholu stále zistiť. Bude to veľkosť intervalu ktorý reprezentuje a tú sme vedeli stále, ak sme k nemu pristupovali. Ostáva ešte, ako zistiť synov vrcholu. Toto vieme vyriešiť použitím objektovo orientovanej implementácie, kde si každý vrchol bude pamätať referencie na svojich synov. Konkrétnejšie sa na to pozrieme v implementácii.

Ak teda máme tieto dve veci vyriešené. Vieme naprogramovať intervalové operácie skoro indenticky ako pri lazy propagation. Časová zložitosť na intervalovú operáciu sa zjavne nezmení, keďže pribudne pri každej návšeteve vrcholu iba konštantne veľa práce, kvôli občasnému vytváraniu synov vrchola. Ak budeme chcieť robiť $q$ intervalových operácii na postupnosti dĺžky $n$ budeme mať časovú zložitosť $O(q \log n)$. Pamäťová zložitosť bude tiež $O(q \log n)$, keďže teraz pri každej návšteve vrcholu teoreticky môžeme vytvoriť nový vrchol.

Riešenie kompresiou súradníc

Oplatí sa spomenúť, že aj keď časová zložitosť operácii riedkeho stromu je pomerne dobrá, tak v praxi bude tento kód niekoľkonásobne pomalší, ako pri implementácii v poli. Často je možné sa vyhnúť tejto pomalej implementácii aj pri veľkých poliach, ak dopredu poznáme začiatky a konce všetkých intervalových operácií. Môžeme využiť takzvanú „kompresiu súradníc“. Stačí totiž uložiť tieto začiatky a konce do osobitnej postupnosti v správnom poradí a na tejto postupnosti postaviť intervalový strom so štandardnou reprezentáciou v poli. Každý interval pôvodnej postupnosti z intervalovej operácie potom budeme vedieť jednoznačne určiť aj v novej postupnosti a teda aj v intervalovom strome. Potom ešte je niekedy potrebné pamätať si pre každý vrchol aj interval, ktorý reprezentuje v pôvodnej postupnosti, aby sme si vedeli dopočítať výsledok tohto vrcholu (teda napríklad súčet na intervale v pôvodnej postupnosti).