IOI Training Week 7 Advanced Data Structures: 1.1 Square-Root (SQRT) Decomposition
IOI Training Week 7 Advanced Data Structures: 1.1 Square-Root (SQRT) Decomposition
Contents
1 Range Minimum Query 1
1.1 Square-root (sqrt) Decomposition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Segment Trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3 Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
4 Problems 6
4.1 Bonus Problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
5 References 6
1
1 RANGE MINIMUM QUERY 2
// end i s e x c l u s i v e
i n t query ( i n t begin , i n t end ) {
i n t l e f t = ( i n t ) c e i l ( b e g i n / ( double ) c h u n k s i z e ) ;
i n t r i g h t = ( i n t ) f l o o r ( end / ( double ) c h u n k s i z e ) ;
i n t ans = 1 << 3 0 ;
i f ( l e f t <= r i g h t ) {
ans = min ( ans , m i n e l e m e n t ( chunks . b e g i n ( ) + l e f t , chunks . b e g i n ( ) + r i g h t ) ) ;
}
i f ( s t a r t % c h u n k s i z e != 0 ) {
ans = min ( ans , m i n e l e m e n t ( a r r −>b e g i n ( ) + begin , a r r −>b e g i n ( ) + ⤦
Ç chunk size ∗ l e f t ) ) ;
}
i f ( end % c h u n k s i z e != 0 ) {
ans = min ( ans , m i n e l e m e n t ( a r r −>b e g i n ( ) + c h u n k s i z e ∗ r i g h t , a r r −>b e g i n ( ) + ⤦
Ç end ) ) ;
}
return ans ;
}
};
The code to update the sqrt decomposition is an exercise left to the reader (you don’t need to submit it).
i n l i n e i n t l e f t ( i n t i d x ) const { return 2∗ i d x + 1 ; }
i n l i n e i n t r i g h t ( i n t i d x ) const { return 2∗ i d x + 2 ; }
// end i s e x c l u s i v e l , as u s u a l
i n t b u i l d ( i n t idx , v e c t o r <e l t y p e > const& a r r , i n t s t a r t , i n t end ) {
i f ( s t a r t + 1 == end ) {
return ( t r e e [ i d x ] = a r r [ s t a r t ] ) ;
}
const i n t mid = ( s t a r t + end ) / 2 ;
return ( t r e e [ i d x ] = min ( b u i l d ( l e f t ( i d x ) , a r r , s t a r t , mid ) , b u i l d ( r i g h t ( i d x ) , ⤦
Ç a r r , mid , end ) ) ) ;
}
i n t query ( i n t s t a r t , i n t end ) {
return query ( s t a r t , end , 0 , 0 , n ) ;
}
The code to update a single element is an exercise left to the reader (you don’t need to submit it).
Furthermore, through the usage of lazy propagation, you can make it so that you can update a range of
elements in the segment tree (also an exercise left to the reader – hint: you need to only add a single piece
of additional information, if your update is adding an integer to a given range).
Similar to sqrt decomposition, you can adapt the segment tree to any associative operation (e.g., range
sum query).
1.3 Notes
Coincidentally, RMQ turns out to be deeply related to another (somewhat rarer, but also standard) problem,
LCA:
Definition 3 (Lowest Common Ancestor (LCA)). Given a rooted tree, T, answer a set of queries of the
form: “What is the vertex of T that is farthest from the root, that is an ancestor of both vertices x and y?”.
The naı̈ve solution for LCA has no setup time, and O(n) query time, and it turns out you can preprocess
LCA into an RMQ problem, and vice versa. You can also use square-root decomposition for LCA, although
of a different form. To learn more about this (and asymptotically better solutions to RMQ), check out the
RMQ and LCA tutorial on Topcoder (c.f. references).
For example, you may have a dynamically changing ordered list of integers, and have to find the index of an
arbitrary integer in the list.
Now, the most commonly used trees in library code are AVL trees and Red-Black trees, because of their
good asymptotic characteristics. However, they are a pain to code, and have a lot of edge cases. Thus,
for competitive programming, we usually implement simpler trees: either scapegoat trees or treaps. In this
discussion, we’ll focus on treaps (because the author considers them easier to implement and understand)5 .
A treap is a binary search tree augmented with a randomized priority value on each of its vertices, and
kept balanced by maintaining a heap (priority queue) structure on its vertices (i.e., each vertex should have a
smaller priority than its children). Because a random heap is balanced, a treap is probabilistically balanced.
The hard part in implementing a treap is in maintaining the heap structure, as one will have to rotate
vertices that violate the heap structure, while maintaining the BST structure.
struct TreapNode {
i n t s t a r t , end ;
int p r i o r i t y ;
TreapNode ∗ k i d s [ 2 ] ;
TreapNode ∗ p a r e n t ;
TreapNode ( ) {}
void i n i t ( i n t s t a r t , i n t end , TreapNode ∗ p a r e n t ) ;
void u p d a t e a u g ( ) {
// w h a t e v e r a u g m e n t a t i o n you need
}
};
// we c a c h e t h e o b j e c t s so t h a t we don ’ t need t o d y n a m i c a l l y
// a l l o c a t e o b j e c t s i n heap ( t h i s i s a l s o known as arena
// a l l o c a t i o n )
TreapNode c a c h e [ 3 0 0 0 0 2 4 ] ;
int c c t r ;
void TreapNode : : i n i t ( i n t s t a r t , i n t end , TreapNode ∗ p a r e n t ) {
k i d s [ 0 ] = k i d s [ 1 ] = NULL;
this −>p a r e n t = p a r e n t ;
this −> s t a r t = s t a r t ;
this −>end = end ;
p r i o r i t y = rand ( ) ;
}
struct Treap {
TreapNode ∗ r o o t ;
Treap ( ) : r o o t (NULL) {}
// d i r : 0 i s l e f t , 1 i s r i g h t
void r o t a t e ( TreapNode ∗ node , i n t d i r ) {
TreapNode ∗ r k i d = node−>k i d s [ d i r ˆ 1 ] ;
i f ( node−>p a r e n t ) {
i f ( node == node−>parent −>k i d s [ 0 ] ) {
node−>parent −>k i d s [ 0 ] = r k i d ;
} else {
node−>parent −>k i d s [ 1 ] = r k i d ;
}
} else {
root = rkid ;
}
r k i d −>p a r e n t = node−>p a r e n t ;
node−>k i d s [ d i r ˆ 1 ] = r k i d −>k i d s [ d i r ] ;
i f ( node−>k i d s [ d i r ˆ 1 ] ) {
5 But
briefly: scapegoat trees remain balanced by maintaining some statistics on their subtrees, and if a subtree is sufficiently
“imbalanced”, it completely reconstructs that subtree. This rebalancing is done rarely enough that operations on a scapegoat
tree remain O(log(n)) time.
2 SELF-BALANCING BINARY SEARCH TREES 5
// u p d a t e a u g m e n t a t i o n
node−>u p d a t e a u g ( ) ;
r k i d −>u p d a t e a u g ( ) ;
}
void add ( i n t s t a r t , i n t end ) {
i f ( r o o t == NULL) {
r o o t = &c a c h e [ c c t r ++];
r o o t −> i n i t ( s t a r t , end , NULL) ;
return ;
}
// l o o k f o r node t o i n s e r t a t
TreapNode ∗p = r o o t ;
TreapNode ∗ node = NULL;
while ( true ) {
i f ( s t a r t < p−> s t a r t ) {
i f ( ! p−>k i d s [ 0 ] ) {
node = p−>k i d s [ 0 ] = &c a c h e [ c c t r ++];
break ;
} e l s e p = p−>k i d s [ 0 ] ;
} else {
i f ( ! p−>k i d s [ 1 ] ) {
node = p−>k i d s [ 1 ] = &c a c h e [ c c t r ++];
break ;
} e l s e p = p−>k i d s [ 1 ] ;
}
}
node−> i n i t ( s t a r t , end , p ) ;
// m a i n t a i n a u g m e n t a t i o n
TreapNode ∗ p2 = p ;
while ( p2 != NULL) {
p2−>u p d a t e a u g ( ) ;
p2 = p2−>p a r e n t ;
}
// r o t a t e
while ( node−>p a r e n t && node−> p r i o r i t y > node−>parent −> p r i o r i t y ) {
i f ( node == node−>parent −>k i d s [ 0 ] ) {
r o t a t e ( node−>parent , 1 ) ;
} else {
r o t a t e ( node−>parent , 0 ) ;
}
}
}
i n t s e a r c h ( TreapNode ∗p , i n t s t a r t , i n t end ) {
// problem − d e p e n d e n t
}
i n t s e a r c h ( i n t s t a r t , i n t end ) {
return s e a r c h ( r o o t , s t a r t , end ) ;
}
};
3 BONUS: MORE INTERESTING DATA STRUCTURES 6
4 Problems
1. Reverse Prime (https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=
show_problem&problem=2657)
2. Frequent Values (https://uva.onlinejudge.org/index.php?option=onlinejudge&page=show_problem&
problem=2176)
3. Census (https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_
problem&problem=2272)
4. Can you answer these queries VIII (http://www.spoj.com/problems/GSS8/)
5. Permutations (https://uva.onlinejudge.org/index.php?option=onlinejudge&page=show_problem&
problem=2520)
5 References
1. RMQ and LCA tutorial https://www.topcoder.com/community/data-science/data-science-tutorials/
range-minimum-query-and-lowest-common-ancestor/