Documente Academic
Documente Profesional
Documente Cultură
Contenidos
Introduccion
set de C++
Mnimo en intervalos (RMQ)
caso estatico: sparse tables
caso dinamico: segment tree
Aplicaci
on: ancestro com
un mas bajo (LCA)
Suma en intervalos
caso estatico: sumas parciales uni- y bi-dimensionales
caso dinamico: binary indexed tree
Introduccion
Qu
e es una estructura de datos?
Una forma de almacenar datos y operar con ellos de modo de
poder calcular eficientemente diversas cantidades.
Puede pensarse como una caja negra donde estan los datos
(posiblemente sujetos a modificaciones), que nos respondera
cierto tipo de preguntas de modo eficiente.
C
omo se usa una estructura de datos en una competencia
de programaci
on?
Las etapas o pasos intermedios del algoritmo muchas veces pueden
verse como subproblemas menores, que debemos resolver para
hallar la respuesta final. Si bien los problemas completos rara vez
se repiten, los subproblemas muchas veces son clasicos, y
conocemos para ellos una estructura de datos adecuada.
Training Camp Argentina - Sexta edici
oin
set de C++
El set de C++ representa un conjunto de elementos, que
podemos pensar como ordenados de menor a mayor. Nos
permite realizar distintas operaciones en O(log N):
insertar y borrar elementos (insert y erase, resp.);
fijarnos si un elemento esta presente (find);
encontrar el primer elemento mayor o mayor o igual que
otro (upper bound y lower bound, respectivamente).
Algunas observaciones:
en un conjunto no puede haber repeticiones;
no controlamos el funcionamiento interno de la estructura,
solo la funcion de comparaci
on;
no podemos modificar elementos (pero s eliminar una version
vieja de un elemento y reemplazarla por una nueva).
Training Camp Argentina - Sexta edici
oin
#i n c l u d e <i o s t r e a m >
#i n c l u d e <s e t >
u s i n g namespace s t d ;
i n t main ( ) {
s e t <i n t > c ;
for ( int
c o u t << c . s i z e ( ) << e n d l ;
f o r ( s e t <i n t > : : i t e r a t o r i t =c . b e g i n ( ) ;
c o u t << i t << ;
i t != c . end ( ) ;
i t ++)
s t r u c t pt {
int x , y ;
p t ( i n t xx =0 , i n t yy =0) {
x=xx ; y=yy ;
};
};
b o o l o p e r a t o r <( c o n s t p t &p1 , c o n s t p t &p2 ) {
d o u b l e a1=a t a n 2 ( p1 . y , p1 . x ) , a2=a t a n 2 ( p2 . y , p2 . x ) ;
i f ( a1 != a2 ) r e t u r n a1 < a2 ;
e l s e r e t u r n p1 . x p1 . x+p1 . y p1 . y < p2 . x p2 . x+p2 . y p2 . y ;
}
[...]
s e t <pt> p ;
p . i n s e r t ( pt (1 ,1) ) ; p . i n s e r t ( pt (2 ,2) ) ;
p . i n s e r t ( pt (2 ,1) ) ; p . i n s e r t ( pt (3 ,5) ) ;
Aplicacion: Dijkstra
1 s e t < p a i r <i n t , i n t > > s ;
2
3 v o i d d i j k s t r a ( i n t s t a r t , i n t d e s t , i n t N) {
4
f o r ( i n t i =0; i <N ; i ++) n [ i ] . d = INF ;
5
n [ s t a r t ] . d = 0; s . i n s e r t ( make pair (0 , s t a r t ) ) ;
6
w h i l e ( ! s . empty ( ) ) {
7
i n t c u r = s . b e g i n ( )>s e c o n d ;
8
i f ( c u r == d e s t ) b r e a k ;
9
f o r ( i n t i =0; i <( i n t ) n [ c u r ] . c . s i z e ( ) ; i ++) {
10
i n t next = n [ cur ] . c [ i ] . f i r s t ;
11
i f ( n [ n e x t ] . d > n [ c u r ] . d+n [ c u r ] . c [ i ] . s e c o n d ) {
12
i f ( n [ n e x t ] . d != INF )
13
s . erase ( make pair (n [ next ] . d , next ) ) ;
14
n [ next ] . d = n [ cur ] . d + n [ cur ] . c [ i ] . second ;
15
s . i n s e r t ( make pair (n [ next ] . d , next ) ) ;
16
}
17
}
18
s . erase ( make pair (n [ cur ] . d , cur ) ) ;
19
}
20 }
para
k = 0, 1, . . .
void
s t i n i t ( i n t m, i n t N, i n t s t ) {
f o r ( i n t i =0; i <N ; i ++) s t [ 0 ] [ i ] = m[ i ] ;
f o r ( i n t k =1; (1<<k )<=N ; k++)
f o r ( i n t i =0; i +(1<<k )<=N ; i ++)
s t [ k ] [ i ] = min ( s t [ k 1 ] [ i ] , s t [ k 1 ] [ i +(1<<(k 1) ) ] ) ;
}
i n t s t q u e r y ( i n t s t , i n t s , i n t e ) {
i n t k = 31
b u i l t i n c l z ( es ) ;
r e t u r n min ( s t [ k ] [ s ] , s t [ k ] [ e(1<<k ) ] ) ;
}
RMQ estatico con sparse tables - O(N log N) init. y O(1) query
10
11
100
1000
101
1001
1010
110
1011
1100
111
1101
1110
1111
#d e f i n e LEFT ( n ) ( 2 ( n ) )
#d e f i n e RIGHT ( n ) ( 2 ( n )+1 )
#d e f i n e NEUTRO 2147483647
i n t oper ( i n t a , i n t b) {
r e t u r n min ( a , b ) ;
}
v o i d i n i t ( i n t n , i n t s , i n t e , i n t rmq , i n t m) {
i f ( s+1 == e ) rmq [ n ] = m[ s ] ;
else {
i n i t ( LEFT ( n ) , s , ( s+e ) / 2 , rmq , m) ;
i n i t ( RIGHT ( n ) , ( s+e ) / 2 , e , rmq , m) ;
rmq [ n ] = o p e r ( rmq [ LEFT ( n ) ] , rmq [ RIGHT ( n ) ] ) ;
}
}
v o i d u p d a t e ( i n t n , i n t s , i n t e , i n t rmq , i n t m, i n t p , i n t v ) {
i f ( s+1 == e ) rmq [ n ] = m[ s ] = v ;
else {
i f ( p < ( s+e ) / 2 )
u p d a t e ( LEFT ( n ) , s , ( s+e ) / 2 , rmq , m, p , v ) ;
e l s e u p d a t e ( RIGHT ( n ) , ( s+e ) / 2 , e , rmq , m, p , v ) ;
rmq [ n ] = o p e r ( rmq [ LEFT ( n ) ] , rmq [ RIGHT ( n ) ] ) ;
}
}
i n t q u e r y ( i n t n , i n t s , i n t e , i n t rmq , i n t a , i n t b ) {
i f ( e <= a | | s >= b ) r e t u r n NEUTRO ;
e l s e i f ( s >= a && e <= b ) r e t u r n rmq [ n ] ;
else {
i n t l = q u e r y ( LEFT ( n ) , s , ( s+e ) / 2 , rmq , a , b ) ;
i n t r = q u e r y ( RIGHT ( n ) , ( s+e ) / 2 , e , rmq , a , b ) ;
return oper ( l , r ) ;
}
}
Luego
Definimos Li como la posici
on de la primera aparicion del
nodo i en el arreglo O, para i = 1, . . . , N
Utilizamos una sparse table para hacer RMQ sobre O con la
operacion nodo a < nodo b Ha < Hb
LCA(i, j) = RMQ[min(Li , Lj ), max(Li , Lj ) + 1)
Training Camp Argentina - Sexta edici
oin
Suma en intervalos
Dado un arreglo de N n
umeros m0 , . . . , mN1 , queremos responder
preguntas de la forma
Cu
al es el valor de S [ i, j) = mi + + mj1 ?
El algoritmo ingenuo recorre todos los elementos en [i, j), y
requiere O(N) en el peor caso;
Si no se van a modificar los valores del arreglo, podemos
preprocesar en O(N) y responder preguntas en O(1);
Si los elementos del arreglo pueden cambiar, podemos
inicializar en O(N log N) y responder preguntas o modificar
valores en O(log N)
v o i d i n i t ( i n t m, i n t sm , i n t N) {
sm [ 0 ] = 0 ;
f o r ( i n t i =1; i <=N ; i ++) sm [ i ] = sm [ i 1]+m[ i 1 ] ;
}
i n t q u e r y ( i n t sm , i n t a , i n t b ) {
r e t u r n sm [ b]sm [ a ] ;
}
b1 d1
X
X
mij
i=a j=c
i n t g e t c f ( i n t idx , i n t b i t ) {
int cf = bit [ 0 ] ;
wh ile ( i d x > 0) {
c f += b i t [ i d x ] ;
i d x &= i d x 1;
}
return cf ;
}
void upd f ( i n t idx , i n t f , i n t b i t ) {
i f ( i d x == 0 ) b i t [ i d x ] += f ;
e l s e w h i l e ( i d x < MAXN) {
b i t [ i d x ] += f ;
i d x += i d x &( i d x ) ;
}
}
v = mi
(viejo)
mi
k
i+2
O1
h
mj O i, i + 2k
j=i
v o i d i n i t ( i n t m, i n t sm , i n t N)
f o r ( i n t i =0; i <N ; i ++) sm [ 0 ] [
f o r ( i n t k =1; (1<<k )<=N ; k++)
f o r ( i n t i =0; i +(1<<k )<=N ;
sm [ k ] [ i ]= o p e r ( sm [ k 1 ] [
}
{
i ] = m[ i ] ;
i ++)
i ] , sm [ k 1 ] [ i +(1<<(k 1) ) ] ) ;
i n t q u e r y ( i n t sm , i n t a , i n t b ) {
i n t RES = 0 , l = ba ;
f o r ( i n t i =0; (1<< i )<=l ; i ++) i f ( l &(1<< i ) ) {
RES = o p e r ( RES , sm [ i ] [ a ] ) ;
a += (1<< i ) ;
}
r e t u r n RES ;
}
Para mayor
comodidad, definimos ai = i N y
bi = (i + 1) N; luego
el i-esimo segmento corresponde a
[ai , bi ), con i = 0, . . . , N
Para cada segmento, guardamos el valor de
Oi =
bO
i 1
mj O[ai , bi )
j=ai
Lazy propagation
En ocasiones puede ser necesaria una estructura que permita
realizar modificaciones sobre muchos elementos a la vez, por
ejemplo actualizar el rango [a, b) fijandolo en un cierto valor o
sumandole a cada elemento una cantidad dada:
Los segment trees discutidos anteriormente soportan estas
operaciones en O(N log N), pero queremos algo mas eficiente;
El poder del segment tree radica en que todo segmento puede
ser representado mediante la uni
on de O(log N) segmentos
disjuntos;
Podemos alcanzar un tiempo de ejecuci
on O(log N) si
ampliamos la estructura para marcar segmentos sobre los
que queremos realizar una operaci
on (a ser efectuada solo en
caso de ser necesario).
Training Camp Argentina - Sexta edici
oin
Persistencia
Nos interesa una estructura de datos con ventana al pasado:
Queremos realizar K operaciones de modificacion de los
datos;
Las consultas deben responderse con la estructura tal como
era justo despues de la k-esima modificaci
on, pero pueden
realizarse despues de haber realizado mas operaciones;
Guardar K copias completas de la estructura de datos es
demasiado costoso.
Observaci
on: Al modificar una estructura, la mayor parte de la
misma no sufre cambios, de modo que puede reutilizarse.
Persistencia (cont.)
Usual
Persistente
Persistencia (cont.)
Mantenemos una lista de races Rk para k = 1, . . . , K
Para responder consultas utilizamos una funcion similar a la
del segment tree usual empezando en la raz Rk deseada y
cambiando LEFT(n) y RIGHT(n) por punteros a (o
identificadores de) los hijos de cada nodo;
Para modificar los datos generamos nuevos nodos en lugar de
modifcar los existentes en la versi
on de referencia;
Cada nuevo nodo tiene un reflejo en la version de referencia
que nos indica cual es el nodo correspondiente al hijo que no
sufre modificaciones;
Por cada modificaci
on generamos solamente O(log N).
Gracias!