Lecture 13: Segment trees – Lazy propagation, binary search Henry Xia, Brandon Zhang based on CPSC 490 slides from 2014-2018 2019-03-04 University of British Columbia CPSC 490: Problem Solving in Computer Science
• Assignment 4 is due tonight. • Presentation 2 topics will be posted shortly. • Presentation 2 dates are the last 3 classes. 1 Announcements • Some kind of visual aid (e.g. slides) will be required for this set of presentations.
• We described a segment tree, a data structure which allows us to: • Update a point x to a new value. • Now, let’s make it more powerful! 2 Last time... • Aggregate the values in a range [ l , r ] .
Given an array A , support the following two operations: element appears. 3 Warmup • Change A [ i ] to k . • Query for the maximum value in [ l , r ] , and the number of times that the maximum
(Combining nodes leħt as exercise.) Just store the max value and the number of times for each node! 4 Warmup – Solution
Given an array A , answer many queries of the following types: 5 Range updates, point queries 1. Output the value of A [ x ] . 2. Add x to all elements in the range A [ l .. r ] .
Let’s just try switching the code for updating and querying. its children’s segments). segments that contain x ). 6 Range updates, point queries The node for the segment [ l , r ] stores the total amount added to this segment (but not to To get the value of A [ x ] , we just add up the values of x ’s ancestors (these are the
Given an array A , answer many queries of the following types: How can we handle both at once? 7 Range updates, range queries 1. Output ∑ A [ l .. r ] . 2. Add x to all the elements in the range A [ l .. r ] .
managing the added values in segments. Idea is similar to range updates, point queries, but now we need to be careful about Let’s be lazy! 8 Range updates, range queries
at in a query. added to this segment that we need to update our children with. • When we need to visit children of this node later, we’ll “push” the lazy update down to its children. 9 Lazy propagation • To add x to everything in [ l , r ] , we’ll update the same O (log n ) segments that we look • We’ll also update a lazy value for these segments: lazy ( i ) = the amount that was
10 Lazy propagation, visualized
10 Lazy propagation, visualized
10 Lazy propagation, visualized
10 Lazy propagation, visualized
10 Lazy propagation, visualized
10 Lazy propagation, visualized
10 Lazy propagation, visualized
10 Lazy propagation, visualized
10 Lazy propagation, visualized
10 Lazy propagation, visualized
10 Lazy propagation, visualized
10 Lazy propagation, visualized
10 Lazy propagation, visualized
10 Lazy propagation, visualized
10 Lazy propagation, visualized
10 Lazy propagation, visualized
10 Lazy propagation, visualized
10 Lazy propagation, visualized
10 Lazy propagation, visualized
10 Lazy propagation, visualized
11 6 13 12 11 10 9 1 7 8 5 4 3 2 Lazy propagation – implementation T = array of 2*N elements lazy = array of 2*N elements, initialized to null def pull(i): T[i] = T[2*i] + T[2*i+1] def push(i): # update node i's children with the lazy value if lazy[i] is not null: T[2*i] += lazy[i] * length(2*i) T[2*i+1] += lazy[i] * length(2*i+1) lazy[2*i] += lazy[i] lazy[2*i+1] += lazy[i] lazy[i] = null
12 6 14 13 12 11 10 9 1 7 8 3 5 2 4 Lazy propagation – implementation def update(x, y, k): # add k to A[x..y] return update(x, y, k, 1, 0, N-1) def update(x, y, k, i, l, r): if r < x or y < l: return # this range is irrelevant if x <= l and r <= y: # range is contained: update the lazy tag T[i] += k * length(i) lazy[i] += k return m = (l + r) / 2 push(i) # accessing children, so need to push! update(x, y, k, 2*i, l, m) update(x, y, k, 2*i+1, m+1, r) pull(i) # update ourselves
13 6 11 10 9 8 1 7 5 4 2 3 Lazy propagation – implementation def query(x, y): return query(x, y, 1, 0, N-1) def query(x, y, i, l, r): if r < x or y < l: # range is irrelevant return 0 if x <= l and r <= y: # range is contained return T[i] m = (l + r) / 2 push(i) # accessing children, so need to push! return query(x, y, 2*i, l, m) + query(x, y, 2*i+1, m+1, r)
Given an array A , answer many queries of the following types: 14 Problem 1 – Slightly difgerent problem 1. Output ∑ A [ l .. r ] . 2. Assign the value x to all the elements in the range A [ l .. r ] .
Use a lazy segment tree! The lazy updates need to be handled a little difgerently: • Instead of summing the lazy values, we’ll assign them. • When pushing, instead of adding the lazy, we’ll set the tree value to the lazy (times the length). 15 Problem 1 – Solution
16 Problem 2 – Colouring Given an array A of k ≤ 60 distinct colours, answer many queries of the following types: 1. Colour the segment [ l , r ] with the colour c . 2. Output the number of distinct colours in the range [ l , r ] .
Each segment tree node stores the set of colours that are in its segment. We can use bitmasks to store the set. Then, a set union is the bitwise OR. • To get the number of distinct colours, make a range query and output the number of elements in the set. 17 Problem 2 – Solution To get the result for a node ( pull ), take the set union of its two children. • To colour [ l , r ] with colour c , assign everything in the range to the set { c } .
and r following operations: 18 Problem 3 – Order-statistic set Implement a multiset storing integers in the range [ 0 , N ] . The multiset should support the • add(x) : add x to the multiset • remove(x) : remove x from the multiset • count(l, r) : output the number of elements in the multiset that are between l • find(k) : return the k th smallest element
We’ve seen many ways to implement this, but one way is with a segment tree! Each node will store the sum of its segment. 19 Problem 3 – Solution • add(x) : add 1 to index x • remove(x) : subtract 1 from index x • count(l, r) : range sum of [ l , r ] What about find ?
• Naive solution—binary search on the answer. To check if x is the k th largest element, But, we can do better... • We can view the segment tree like a binary search tree. • Start at the root node. If the sum of our leħt child is k , recurse on the right child, else recurse on the leħt. 20 Problem 3 – Solution check if the range sum [ 0 , x ] is larger or smaller than k . • O (log n ) iterations of binary search, each of which performs an O (log n ) query ⇒ O (log 2 n ) in total
• Naive solution—binary search on the answer. To check if x is the k th largest element, But, we can do better... • We can view the segment tree like a binary search tree. else recurse on the leħt. 20 Problem 3 – Solution check if the range sum [ 0 , x ] is larger or smaller than k . • O (log n ) iterations of binary search, each of which performs an O (log n ) query ⇒ O (log 2 n ) in total • Start at the root node. If the sum of our leħt child is < k , recurse on the right child,
Implement a multiset supporting all the previous operations, as well as 21 Problem 4 – More statistics • find_next(x) : return the smallest element in the set larger than x
• We’ll “go up” then “go down” in the segment tree. • If the range sum of our segment is greater than 0, then the answer is in our segment. Go down using the method described in the last problem. • Otherwise, we need to find a bigger segment to the right of us with a nonzero sum, so we’ll “go up”. • If we’re a right child, go to the node to the right of our parent. • Otherwise, go to our parent. Before starting at a leaf, remember to push down! 22 Problem 4 – Solution We can still do this in O (log 2 n ) naively, but let’s try to find something faster... • We need to find the smallest y such that the range sum of [ x + 1 , y ] is greater than 0. • Start at the leaf node representing x + 1. We only go up and down once, and the depth of the tree is O (log n ) , so the runtime is O (log n ) .
Q customers will enter the store. Each customer has a difgerent amount of money v i , and customer encounters an item, they will buy as many copies of it as they can afgord. Source: ACM-ICPC Pacific Northwest Regional 2016 23 Problem 5 – Shopping Input : a store with N items in a row, with difgerent prices p i . will walk through the store from leħt to right between items l i and r i . Whenever a Output : for each customer, the amount of money they’ll have leħt aħter exiting.
Recommend
More recommend