Sunteți pe pagina 1din 16

Liceul Academiei de Stiinte a Moldovei

Cercetare știintifico-practica
Disciplina: Informatica

Tema: Arbori de intervale

Autor: Trubca Dmitri, cl. a XI-a ,,D”


Conducator știintific: Miron Raisa
Coordonator: Craveț Ala

Chisinau, 2018
Cuprins
Capitolul I. Prezentarea arborilor de intervale........................................................................................3
1.1 Ce reprezinta arborii de intervale .......................................................................................................3
1.2 Implementare........................................................................................................................................4
1.4.1 Construirea arborelui.........................................................................................................4
1.4.1 Interogarea unui interval...................................................................................................5
1.4.1 Modificare.........................................................................................................................5
1.3 Lazy Propagation (actualizarea unui interval).....................................................................................6
1.4 Optimizari/simplificari.........................................................................................................................7

Capitolul II. Analiza problemelor..............................................................................................................8


Datorii...................................................................................................................................................8
339D (codeforces)..............................................................................................................................10
Inv.......................................................................................................................................................11
Colonia de furnici (474F - Codeforces)..............................................................................................12
Schi.....................................................................................................................................................13
Probleme propuse................................................................................................................................14

Bibliografie.................................................................................................................................................15

2
Capitolul I. Prezentarea arborilor de intervale
Pentru a înțelege arborii de intervale considerăm următoare problemă:
Fie un vector A cu N elemente. Asupra lui se vor face următoarele operații:
1) Să se determine suma elementelor din intervalul [a,b].
2) Valoarea elementului de pe poziția a va deveni b.
O soluție simplă pentru operația de tipul 1 ar fi de parcurs toate elementele vectorului A cu indice
cuprinse între segmentul [a,b] și de calculat suma. Pentru operația de tipul 2 atribuim A[a] = b. Prima
operație are complexitatea O(n), iar a doua O(1).
O altă soluție ar fi de creat un tablou S care ar păstra suma elementelor de la început pîna la A[i], pentru
indicile i a tabloului S. Astfel, suma elementelor poate fi calculată în O(1) (suma = S[b] - S[a-1]), însă
operația de modificare a unui element va rula acum în O(n), întrucît în cel mai rau caz va trebui sa
modificam tot tabloul S.
Pentru a efectua ambele operații într-o complexitate optimă O(log n) putem folosi arborii de intervale.

1.1 Ce reprezinta arborele de intervale


Arborele de intervale este un arbore binar utilizat pentru stocarea informației de pe intervalele unui
vector. Fiecare nod din arbore contine informatia specifica unui anumit interval (ex: suma, minim,
maxim…). Luam in considerare un tablou A de dimensiuni N și arborele corespunzator lui T:
1. Rădăcina arborelui T va contine informatia specifica intregului tablou A[1..N].
2. Fiecare nod terminal (frunza) a arborelui va reprezinta un singur element al tabloului A[i], 1 ≤ i ≤ N.
3. Nodurile interne a arborelui vor reprezinta reuniunea intervalelor A[i : j], unde 1 ≤ i < j ≤ N.
Radacina arborelui de intervale contine informatia (suma/max/min) specifica intergului tablou A[1..N].
Ea la rindul sau este impartita in 2 jumatati de intervale, astfel copiii radacinii vor reprezinta intervalele
A[1 : N/2] si A[N/2 + 1 : N]. Astfel, la fiecare pas intervalul este impartit in 2 jumatati si cei 2 copii a
nodului reprezinta jumatatile respective. Din acest motiv inaltimea arborelui va fi log 2 N .

3
Odata ce arborele de intervale este creat, structura lui nu poate fi modificata. Noi putem actualiza
valorile corespunzatoare nodurilor, dar nu si structura arborelui. Arborelui de intervale ii sunt specific 2
operatii:
1) Actualizare: Pentru a actualiza elementul tabloului si pentru a reflecta modificarea corespunzatoare in
arborele de intervale
2) Interogare: Cu ajutorul acestei operatii putem interoga un interval anumit si returna raspunsul la
interogarea respectiva (de exemplu: minim/maxim/suma segmentului respectiv).
Implementare
Intrucit arborele de intervale este un arbore binar, pentru reprezentarea lui poate fi folosit un tablou
unidimensional. Inainte de construirea arborelui, trebuie sa decidem ce vom pastra in nodurile lui. De
exemplu, daca trebuie sa gasim suma elementelor cuprinse intr-un anumit interval, atunci fiecare nod
(cu exceptia nodurilor terminali) va pastra suma nodurilor copiilor sai.
Arborele binar poate fi construit recursiv, incepind cu frunzele lui, ridicindu-se catre radacina arborelui
si actualizind valorile nodurilor corespunzatoare. Fiecare frunza reprezinta un singur element. La fiecare
etapa, valorile nodurilor copiilor sunt folosite pentru a actualiza valoarea parintelui. Fiecare nod intern
va reprezenta reuniunea intervalelor copiilor sai. Deci, crearea arborelui se va termina dupa actualizarea
nodului radacina.
Pentru actualizarea unui element, trebuie sa cautăm frunza arborelui ce conține elementul respectiv.
Acest lucru poate fi realizat daca incepem de la nodul radacina, si mergem recursiv spre copilul sting
sau drept, in dependenta de intervalul care contine elementul cautat. Odata ce ajungem la un nod
terminal (frunza) actualizam valoarea nodului corespunzator, dupa ce recursiv modificam valoarea
tuturor nodurilor de pe drumul parcurs, incepind de la nodul frunza pina la nodul radacina.
Pentru a efectua o interogare pe arbore, selectam intervalul L (left) pina la R (right) pentru care trebuie
sa facem interogarea. Incepind cu nodul radacina verificam daca intervalul reprezentat de nod intra
complet in diapazonul L - R. Daca intervalul reprezentat de nod intra complet in segmental [L .. R],
returnam valoarea nodului, in caz contrar exploram copii lui.
Pentru rezolvarea problemei propuse pe pagina anterioara, in primul rind construim arborele de
intervale. Intrucit intrebarea este de a returna suma elementelor dintr-un anumit interval, fiecare nod al
arborelui va contine suma elementelor tabloului cu indicele cuprinse in intervalul reprezentat (suma
copiilor sai).
Crearea arborelui
Pentru crearea arborelui de intervale folosim procedura build care primeste 3 parametri:
st (stingul) , dr (dreptul) - indicii de inceput si sfirsit a segmentului reprezentat de nodul curent
nod - indicele nodului curent in arborele de intervale. Initial nodul radacina are indicele 1.
Pentru a crea arborele de intervale pentru tabloul A[1..N], apelăm funcția build(1, N, 1).
//Procedura recursiva pentru crearea arborelui de intervale

4
Asa cum se arata in codul de mai sus, pornim de la nodul radacina si apelam procedura pentru copilul
sting si drept, pina ce nu ajungem la un nod terminal. De la frunze ne intoarcem recursiv la radacina si
actualizam valorile tuturor elementelor de pe traseul dat. “nod” - reprezinta indicele nodului curent, iar
intrucit arborele de intervale este un arbore binar, 2*nod va reprezenta indicele copilului sting si 2*pos
+ 1 - indicele copilului drept.
Complexitate: O(N).
Actualizare
Pentru actualizarea unui element, ne uitam la intervalul in care se afla elementul respectiv, si apelam
procedura corespunzatore pentru copilul sting sau drept (in dependenta de interval).
Pe linga parametrilor similari procedurii build, procedura update mai primeste 2 parametri:
idx - indicele elementului pe care dorim sa-l actualizam din tabloul pe baza caruia este creat arborele
val - valoarea pe care o vom atribui elementului
//Procedura recursiva pentru actualizarea unui element din arbore

Complexitate: O(logN)
Interogarea unui interval
Pentru interogarea unui anumit interval, verificam 2 conditii:
1. Intervalul reprezentat de nod se afla complet in intervalul dat
2. Intervalul reprezentat de nod se afla partial in interiorul intervalului cautat
Dacă intervalul reprezentat de un nod se află complet în intervalul cautat, vom returna valoarea nodului care
este suma tuturor elementelor în intervalul reprezentat de nod. Daca intervalul reprezentat de un nod este
parțial în interiorul și parțial în afara intervalului dat, vom returna suma interogarii copilului din stânga (daca
intervalul reprezentat de copilul sting se afla partial/complet in interiorul intervalului cautat) și a copilului
drept. Complexitatea interogării va fi O (logN).
Parametrii formarmali l, r reprezinta capetele intervalului [l, r] supus interogarii.
//Procedura recursiva pentru interogarea unui anumit interval a arborelui

5
Lazy Propagation (actualizarea unui interval in logN)
Uneori, este necesar de actualizat un interval [l, r], în loc de un singur element. Una din solutii este
actualizarea tuturor elementelor unul câte unul. Complexitatea acestei abordări va fi O (NlogN), deoarece in
tablou sunt N element și actualizarea unui singur element va dura O (logN). De asemenea, este posibila
optimizarea la O(n).
Pentru a evita apelurile multiple a procedurii de actualizare, putem modifica procedura pentru a lucra cu
intervale. Pentru a optimiza aceasta operatie, vom actualiza intervalul doar atunci cind este necesar. Pentru
aceasta avem nevoie de un tablou ajutator lazy[], de aceeasi dimensiune ca si arborele de intervale.
Initial, toate elementele tabloului lazy[] vor fi 0, reprezentind faptul ca nu exista actualizari in asteptare.
Daca exista un element lazy[k] diferit de zero, atunci inseamna ca cu valoarea acestui element trebuie sa
actualizam nodul k in arborele de segmente inainte de a efectua orice operatie de interogare.
Pentru a actualiza un interval, vom pastra in minte 3 lucruri:
- Daca nodul arborelui de segmente actual are o actualizare in asteptare (in tabloul lazy[]), atunci mai intai
adaugam actualizarea in asteptare la nodul curent.
- Daca intervalul reprezentat de nodul curent se afla complet in intervalul de actualizare, actualizam nodul
curent si tabloul lazy[] pentru copiii nodului respectiv.
- Daca interval reprezentat de nodul curent se suprapune cu intervalul de actualizare, atunci actualizam
nodurile conform algoritmului de actualizare studiat anterior.
//Functie recursive pentru actualizarea unui interval (lazy propagation)

6
Complexitate: O(logN)
Deoarece am introdus tabloul lazy[] pentru actualizarea optimala a unui interval, va trebui sa schimbam si
functia de interogare. Singura modificare pe care trebuie sa o facem este sa verificam daca exista o operatie
de acutalizare in asteptare pe acel nod. Daca exista o operatie de actualizare in asteptare, mai intii actualizam
nodul, dupa ce actionam conform functiei anterioare de interogare.

//Functie recursiva pentru interogarea unui interval (lazy propagation)

Optimizari si simplificari
7
In continuare, pentru optimizarea programelor in locul impartirii la 2 (mid = (st+dr)/2) vom utiliza operatie
shr (mutam bitii in partea dreapta - mid = st+dr >> 1). De asemenea, pentru simplificarea codului definim cu
ajutorul comenzei #define indicele copilului sting (L) si drept (R): “#define L 2*nod” “#define R L|1”.
Observam ca pentru construirea arborelui nu este necesar intotdeauna sa cream procedura build. Pentru
economisirea timpului uneori este mai comoda crearea lui, adaugind cite un element in arbore cu ajutorul
procedurii update. Aceasta abordare va rula intr-o complexitate mai mare (O(logN)), insa din cauza lipsei
tabloului initial, se va economisi memorie.
De asemenea, pentru economisirea memoriei, timpului si simplificarea codului in problemele analizate
ulterior vom declara variabilele l, r, idx, val (care indica intervalul pentru interogare/actualizare/
indicele/valoarea cu care vom actualiza elementul) - globale.

Capitulul 2. Analiza problemelor.


2.1 Datorii
Intr-o companie, clientul care cumpara un calculator in ziua X, poate returna banii oricand doreste acesta.
Astfel, aproape in fiecare zi, se prezinta diversi clienti care achita integral sau partial un sistem de calcul
cumparat in zilele anterioare. 
Vi se solicita sa raspundeti la urmatoarele intrebare: ce suma de bani a ramas inca neachitata luand in
considerare achizitiile facute de clienti in zilele l, l+1, l+2... r-1, r (0 < l ≤ r ≤ N). Se stie ca niciodata nu s-au
cumparat doua calculatoare in aceeasi zi.
Se dau n,m, un sir A1, A2, …, An si m operatii. Ai (1 ≤ Ai ≤ 1000, 1 ≤ i ≤ n) reprezinta suma de bani inca
neachitata pentru o comanda efectuata in ziua i.
Operatiile pot fi de 2 feluri:
 0 - (achitare - se scade o valoare din suma restanta a unei zile anume),  urmata de doua numere
intregi idx, nr (idx - ziua, nr - valoarea)
 1 - (interogare - se cere suma tuturor sumelor restante ale unui interval de zile [l, r], luind in vedere
achitarile efectuate anterior).

Date de intrare:
8
Prima linie contine 2 numere n,m. Urmatoarea - n numere intregi reprezentind sirul Ai.
Urmatoarele m linii descriu cite o operatie si contin cite 3 numere, primul nr. indicind tipul operatiei.
Operatia de tip 0 este urmata de 2 numere integi idx, nr (idx - ziua, nr - valoarea)
Operatia de tip 1 este urmata de 2 numere integi l, r, ce reprezinta intervalul de interogare.

Descrierea solutiei:
Din cauza restrictiilor mari (m ≤100000 ) o abordare brute-force va iesi din timp. Observam, ca problema
data poate fi rezolvata intr-o complexitate optima cu ajutorul arborelui de intervale.
Cream un arbore, frunzele caruia vor reprezenta suma de bani neachitata pentru comanda efectuata in ziua i
(i = 1..n), iar nodurile interne vor pastra suma copiilor sai. Astfel, nodul radacina va pastra suma de bani
neachitata totala.
La fiecare operatie de tip 0 actualizam valoarea nodului corespunzator zilei idx.
Pentru operatia de tip 1 efectuam o interogare pe intervalul [l, r] si returnam suma nodurilor ce se contin in
acest interval.
Complexitatea algorimului O(m∗logN )
Implementarea algoritmului descris este reprezentata mai jos:

9
10
2.2 Xenia si operatiile pe biti (339D)
Se da o secventa a constituita din 2n numere nenegative: a1, a2, …, a2n . Secventei date ii este caracteristica
valoarea v, care se calculeaza prin citeva itaratii. La prima iterare se calculeaza o secventa noua, formata din
operatia OR a elementelor vecine: a1 or a2, a3 or a4, …, a2n-1 or a2n. La a doua iterare operatie de disjunctie
XOR a elementelor vecine a secventei obtinute dupa prima iterare. La a treia - operatia OR a elementelor
vecine din secventa obtinuta dupa a doua iteratie s.a.m.d. Operatiile OR si XOR se alterneaza. La sfarsit, se
obtine o secventa continuta dintr-un singur element, care va fi v.
Consideram un exemplu. a = (1,2,3,4). Transfomarile secventii vor fi (1, 2, 3, 4)  → (1 or 2 = 3, 3 or 4 = 7)  
→  (3 xor 7 = 4). In rezultat v = 4.
De asemenea, sunt date m interogari, de schimbare a secventei
Primul rind contine 2 numere n, m. Urmeaza secventa initiala ce continue n elemente, si m linii ce contin 2
numere p,b, care inseamna ca trebuie efectuata atribuirea ap = b si afisata valoarea v a secventei obtinute.
Solutie: Pentru rezolvarea problemei vom folosi structura de date arbori de intervale.
In frunzele arborelui vom pastra valorile a i. In nodurile distanta la care pina la frunze este 1 (impara), vom
pastra OR a numerelor din frunze, care sunt copii acestui nod. In mod similar, in nodurile, distanta de la care
pina la frunze este para, vom pastra xor a numerelor, pastrate in copii lor. S.a.m.d. Astfel, radacina arborelui
va contini valoarea ceruta v.
Pentru efectuarea modificarii unui element din secventa, este destul sa gasim drumul de la virf pina la frunza
ce reprezinta pozitia ceruta pentru modificare, sis a recalculam valorile numai in acele noduri, care se afla pe
drumul respectiv. Astfel, complexitatea unei modificari va fi O(n). Memoria necesara (2n).

11
2.3 Inv
Se dă un şir S de lungime n cu numere întregi. Numim o inversiune o pereche de indici (i, j) astfel încât 1 ≤ i
< j ≤ n şi S[i] > S[j]. Să se determine câte inversiuni sunt în şirul dat.
Restrictii: n ≤ 100 000
Solutie: Intrucat numarul de elemente a sirului este destul de mare, a abordere brute-force ( n2 ) va iesi din
timp. Problema se poate rezolva intr-o complexitate optima O(n∗logN ) cu ajutorul arborelui de intervale.
Atribuim fiecarui element a sirului indicele lui din sir (se poate realiza cu ajutorul unui struct), dupa ce
sortam elementele crescator dupa valoare, iar in cazul egalitatii valorilor, crescator dupa indicele din sirul
original. Ramane sa parcurgem sirul sortat de la stinga la dreapta, si pentru fiecare element sa interogam cite
elemente cu indice strict mai mare ca el am introdus anterior in arbore, incrementam solutia cu acest numar,
dupa care introducem si elementul actual in arbore (atribuind frunzei corespunzatoare indicelui elementului
valoarea 1).
Implementarea acestui algoritm:

12
2.5 Colonia de furnici (#474F Codeforces)
Mole a gasit o colonie de furnici aliniate la rand, fiecare avind puterea si.
Pentru a-si diversica cina, Mole organizeaza “Jocuri de foame” pentru furnici. El alege 2 numere l si r si
fiecare pereche de furnici aflate pe pozitiile intre l si r (inclusiv) lupta. Atunci cind lupta doua furnici i si j,
furnica i primeste puncte numai daca si divide sj. Daca furnica a obtinut r - l puncte (cite 1 punct in fiecare
lupta), ea nu va fi mincata. Se da t intervale [l, r] si se cere de aflat cite furnici vor fi mincate daca cirtita va
alege acest interval.
Solutie: Pentru fiecare subsecventa [L, R] trebuie sa determinam cite numere avem, ce sunt egale cu cel mai
mare divizor comun (cmmdc) a (sL, sL + 1, ..., sR).
Astfel, problema se reduce la determinarea c.m.m.d.c a unui interval a tabloului - operatie ce poate fi
executata intr-o complexitate optima (logN), in cazul folosirii structurii de date arbori de intervale.
Frunzele arborelui vor contine elementele tabloului, iar radacina lui c.m.m.d.c a intregului tablou.
Pentru determinarea numarului de elemente egale cu c.m.m.d.c, cu indice cuprinse in acest interval, folosim
structura de date (predefinita) map din C++. Astfel, pentru valoarea gcd a segmentului determinata anterior,
vom calcula cite numere egale cu gcd se afla in acest interval (utilizind functiile lower_bound/upper_bound).
Raspunsul la o interogare va fi R - L + 1 - nrValori, unde nrValori este numarul de elemente din subsecventa
egale cu c.m.m.d.c.
Complexitatea aceste solutii este O ¿.

13
2.4 Schi
La un concurs de schi, are loc o proba contra-cronometru, concurentii trebuind sa termine traseul in cel mai
scurt timp cu putinta. Acestia vor evolua consecutiv si, dupa finish, li se va comunica locul ocupat in
clasamentul intermediar. Mai exact, dupa evolutia celui de-al p-lea concurent, acesta va sti locul sau in
clasamentul format de primii p concurenti. Cunoscand pozitiile intermediare, se cere sa se determine
clasamentul final al competitiei.
Solutie: Observam ca pozitia intermediara a concurentului cu numarul de ordine n va corespunde
intotdeauna locului ocupat de el in clasamentul final.
Cream un arbore de intervale constituit din n frunze cu valoare 1, iar nodurile interne vor contine suma
copiilor sai. Incepind de la sfirsit, la fiecare pas actualizam valoarea frunzei a[i] egala cu 1, numarul ei din
arbore reprezentind locul final al concurentului i.
Astfel, la actualizarea concurentului cu primul indice va ramane doar o singura frunza egala cu 1.
Implementarea algoritmului:

14
Probleme analizate anterior:
- Datorii (https://www.infoarena.ro/problema/datorii)
- 339D (http://codeforces.com/contest/339/problem/D)
- Inv (https://www.infoarena.ro/problema/inv)
- Schi (https://www.infoarena.ro/problema/schi)
- 474F (http://codeforces.com/problemset/problem/474/F)

Probleme propuse spre rezolvare:


- 356A (http://codeforces.com/problemset/problem/356/A)
- Subsir crescator maximal (https://www.infoarena.ro/problema/scmax)
- 920F (http://codeforces.com/contest/920/problem/F)
- Biscuiti (https://www.infoarena.ro/problema/biscuiti)
- Hotel (https://infoarena.ro/problema/hotel)
- Trade (http://varena.ro/problema/trade)

15
Bibliografie

1. hackerearth.com
2. infoarena.ro
3. codeforces.com

16

S-ar putea să vă placă și