CPSC 490: Problem Solving in Computer Science 1 Range-minimum query - - PowerPoint PPT Presentation

cpsc 490 problem solving in computer science
SMART_READER_LITE
LIVE PREVIEW

CPSC 490: Problem Solving in Computer Science 1 Range-minimum query - - PowerPoint PPT Presentation

Lecture 12: Segment trees Henry Xia, Brandon Zhang based on CPSC 490 slides from 2014-2018 2019-02-28 University of British Columbia CPSC 490: Problem Solving in Computer Science 1 Range-minimum query Given an array A of N 10 6 numbers,


slide-1
SLIDE 1

CPSC 490: Problem Solving in Computer Science

Lecture 12: Segment trees

Henry Xia, Brandon Zhang

based on CPSC 490 slides from 2014-2018

2019-02-28

University of British Columbia

slide-2
SLIDE 2

Range-minimum query

Given an array A of N ≤ 106 numbers, answer Q ≤ 106 queries of the form “What is the minimum number in the subarray A[l..r]?”

1

slide-3
SLIDE 3

Range-minimum query

Recall that we solved this using binary jumping: f(i, p) = the minimum number in A[i..i + 2p − 1]. f(i, 0) = . . . , f(i, p) = . . . min A[l..r] = min{f(l, q), f(r − 2q + 1, q)}.

2

slide-4
SLIDE 4

Range-minimum query, point updates

Given an array A of N ≤ 106 numbers, answer Q ≤ 106 queries and updates of the following type:

  • 1. Output the minimum number in the subarray A[l..r].
  • 2. Update A[i] to k.

Naive solution: O 1 per update, O N per query. Binary jumping doesn’t work! Very slow to update. Let’s try a difgerent way of splitting our array into chunks...

3

slide-5
SLIDE 5

Range-minimum query, point updates

Given an array A of N ≤ 106 numbers, answer Q ≤ 106 queries and updates of the following type:

  • 1. Output the minimum number in the subarray A[l..r].
  • 2. Update A[i] to k.

Naive solution: O(1) per update, O(N) per query. Binary jumping doesn’t work! Very slow to update. Let’s try a difgerent way of splitting our array into chunks...

3

slide-6
SLIDE 6

Range-minimum query, point updates

Given an array A of N ≤ 106 numbers, answer Q ≤ 106 queries and updates of the following type:

  • 1. Output the minimum number in the subarray A[l..r].
  • 2. Update A[i] to k.

Naive solution: O(1) per update, O(N) per query. Binary jumping doesn’t work! Very slow to update. Let’s try a difgerent way of splitting our array into chunks...

3

slide-7
SLIDE 7

Segment tree design

Let’s assume that N is a power of two (if not, pad the array until it is). We’ll make a perfect binary tree where each node represents a segment of the array.

  • The leaves represent segments with one element.
  • The root represents the whole array.
  • Each non-leaf represents the union of the segments of its two children.
  • Perfect binary tree with lg N layers, so 1 + 2 + 4 + · · · + N = 2N − 1 nodes in total.

Each node in the tree will store the answer for that segment (in our range-minimum query problem, the minimum for that segment).

4

slide-8
SLIDE 8

Segment tree design

Let’s assume that N is a power of two (if not, pad the array until it is). We’ll make a perfect binary tree where each node represents a segment of the array.

  • The leaves represent segments with one element.
  • The root represents the whole array.
  • Each non-leaf represents the union of the segments of its two children.
  • Perfect binary tree with lg N layers, so 1 + 2 + 4 + · · · + N = 2N − 1 nodes in total.

Each node in the tree will store the answer for that segment (in our range-minimum query problem, the minimum for that segment).

4

slide-9
SLIDE 9

Segment tree design

How do we effjciently compute the answer for a segment? If we’re at a node representing the segment [l, r], then our leħt child has the minimum of the segment [l, m] and our right child has the minimum of the segment [m + 1, r] (where m = (l + r)/2). The minimum of our segment is the minimum of the values at our two children!

5

slide-10
SLIDE 10

Segment tree design

How do we perform updates (what do we need to change when we update A[i] to k)? Obviously we need to update the leaf node for the ith element. The parent of this node depends on this, so we’ll need to update it as well, then update its parent, and so on until we reach the root. O(log N) to update, since we only update on the path from leaf to root in a tree with depth lg N.

6

slide-11
SLIDE 11

Point update, visualized

7

slide-12
SLIDE 12

Point update, visualized

7

slide-13
SLIDE 13

Point update, visualized

7

slide-14
SLIDE 14

Point update, visualized

7

slide-15
SLIDE 15

Point update, visualized

7

slide-16
SLIDE 16

Segment tree design

What about querying for the minimum element in the range [x, y]? We can decompose this range into subranges in the segment tree. We’ll find the subranges recursively:

  • Start at the root and recurse downards.
  • Suppose the node we’re at represents the segment [l, r]. There are 2 cases:
  • If [l, r] ⊂ [x, y] then return the value of the node.
  • Otherwise, recurse on the children that aren’t disjoint from [x, y] and combine the answer

(in this case, take the min).

We can prove that at each level, we’ll consider at most 4 nodes ⇒ O(log n) per query.

8

slide-17
SLIDE 17

Range query, visualized

9

slide-18
SLIDE 18

Range query, visualized

9

slide-19
SLIDE 19

Range query, visualized

9

slide-20
SLIDE 20

Range query, visualized

9

slide-21
SLIDE 21

Range query, visualized

9

slide-22
SLIDE 22

Range query, visualized

9

slide-23
SLIDE 23

Range query, visualized

9

slide-24
SLIDE 24

Range query, visualized

9

slide-25
SLIDE 25

Segment tree initialization

  • Naive way: call update for each element (O(N log N))
  • Faster way: fill from the leaves upwards, layer by layer (O(N))

Overall, our RMQ problem runs in O(N + Q log N).

10

slide-26
SLIDE 26

Segment tree implementation

How do we implement these operations? First, to store the tree:

  • Store the tree nodes in an array T of length 2N.
  • The root is T[1].
  • For a node i, its children are 2i and 2i + 1, and its parent is ⌊i/2⌋.
  • The leaf nodes are T[N..2N − 1].

11

slide-27
SLIDE 27

Segment tree implementation

1

T = array of length 2*N # N is a power of two

2 3

def pull(i): # update node i with its children's values

4

T[i] = min(T[2*i], T[2*i+1])

5 6

def init(A): # initialize the segment tree with array A

7

# fill leaves

8

for i = 0 .. N-1:

9

T[N+i] = A[i]

10

# fill rest of tree

11

for i = N-1 .. 1:

12

pull(i)

12

slide-28
SLIDE 28

Segment tree implementation

1

def update(i, k): # change the ith element to k

2

# update the leaf

3

v = N+i

4

T[v] = k

5

# propagate changes up to the root

6

v = v / 2

7

while v > 0:

8

pull(v)

9

v = v / 2 # v = parent(v)

13

slide-29
SLIDE 29

Segment tree implementation

1

def query(x, y): # query for the min value in [x, y]

2

return query(x, y, 1, 0, N-1)

3 4

def query(x, y, i, l, r): # i is the node index, and its segment is [l, r]

5

if r < x or y < l: # [l, r] completely outside [x, y] (irrelevant)

6

return ∞ # the identity element for min

7

if x <= l and r <= y: # [l, r] contained in [x, y] (don't need to recurse)

8

return T[i]

9

# recurse on children

10

m = (l + r) / 2

11

return min(query(x, y, 2*i, l, m), query(x, y, 2*i+1, m+1, r))

14

slide-30
SLIDE 30

Segment tree

Segment trees are very flexible! We can store any kind of data in our nodes, and combine them in (almost) any way (e.g. gcd, sets and set union, polynomials and addition).

15

slide-31
SLIDE 31

Problem 1 – Classic problem

Answer Q queries on an array A of the following types:

  • 1. Update A[i] to k.
  • 2. Query for the maximum-sum subarray in A[l..r].

16

slide-32
SLIDE 32

Problem 1 – Solution sketch

In a node, store four values:

  • The sum of all the elements in the segment.
  • The maximum sum of a subarray which starts at the leħt endpoint.
  • The maximum sum of a subarray which ends at the right endpoint.
  • The maximum sum of any subarray.

At a node, its best subarray could lie entirely in the leħt child, entirely in the right child, or it could go across. Use the statistics we store for our children to compute our own!

17

slide-33
SLIDE 33

Problem 2 – Increasing triples

Input: an array A with N ≤ 106 integers. Output: the number of triples of indices (i, j, k) satisfying i < j < k and A[i] < A[j] < A[k].

Source: Codeforces 61E

18

slide-34
SLIDE 34

Problem 2 – Solution

Idea: for each index j, count the number of triples where j is the middle index.

  • For all j, we need to count pairs (i, k) such that i < j < k and A[i] < A[j] < A[k].
  • Let less(j) be the number of indices i with i < j and A[i] < A[j], and let greater(j) be the

number of indices k with j < k and A[j] < A[k].

  • We just need to compute less(j) · greater(j) for each j, and sum them up.
  • Let’s count these separately in two passes.
  • We’ll store a segment tree indexed by the values. Then, a range sum will tell us the

number of array elements in that range.

  • For less(j), process j from leħt to right. Initially, the segment tree stores all 0’s.
  • Add 1 to the A[j]th index in the segment tree. Then, less(j) is the range sum from

[0, A[j] − 1].

  • greater(j) is similar (going from right to leħt).

Time complexity: O(n log n)

19