Problem Solving(Segment Trees — part 2)
By: Gaurav Tiwari
Tags: Educational, Problem Solving, Competitive Programming

photo from unsplash

Hello everyone, hope you people are safe and healthy. In my previous blog we have already started with the segment trees and now, I will discuss two interesting problems in this and see how we can approach to these problems using segment trees. Let’s quickly dive into the problems.

Problem #1: Given a permutation p of n elements, find for each i the number of j such that j < i and pj > pi.

See fig 1 for better understanding and fig 2 which we are supposed to calculate for each index i, satisfying the given criteria in the problem statement.

Fig 1 : A permutation of 5 elements

Fig 2 : Number of greater elements for each index i to its left.

Let’s discuss the approach:

Approach #1: We can do brute force i.e, for each index i we simply run the loop of j such that j < i and count the number of elements pj which are greater than pi, so time complexity will be ~O(N²) and this will not work for the constraint like N in range ~ 100000.

Can we do better?

Approach #2: you people might be thinking how we can use segment tree here and what query we have to do for building the answer for each i. Let’s make a segment tree of sum of range, where at the leaf node we mark 1 for all the numbers 1 to n. See the fig 3 for better visualisation.

fig 3 : permutation of n elements are present at leaf nodes as an index of an array.

Now, you people might be thinking what is the purpose of building a sum segment tree over the range, it will become clear soon. So, we are interested in only the left part of each index i, to count the number of greater elements than pi, we will walk in the array from the end and the number at each index i denote the index of leaf nodes in the segment tree, so we will build the answer for each index by query over the range [pi + 1, n] and this will return the number sum of ones in this range and this sum will be the answer for this index i, then before going to the next index we will do a point update at this index i and setting it to 0. This ensures that this number will never be the part of answer for some other index j (j > i).

Let’s take an example, as the last number in our array is 2 then we will query for range [3, n] and after this we set it to 0 before moving ahead, fig 4 will clear the query part and point update part.

fig 4 : query for range [3, 5] and 3 will return as answer for this range.

Fig 5 will clear the point update.

Fig 5: represent the point update for index 2 and maintain the sum while backtracking.

Finally, what is the time complexity for this problem?

We have already seen that query for a range can be answered in ~O(log N) and point update can also be answered in ~O(log N) as height of the segment tree is log N and we will be doing both operations for each index i, so total of N queries and N updates will happen therefore overall complexity will be ~O(N log N).

Problem #2 : Given an array of 2n numbers, each number from 1 to n in it occurs exactly twice. We say that the segment y is nested inside the segment x if both occurrences of the number y are between the occurrences of the number x. Find for each segment i, how many segments that are nested inside it.

Let’s take an example:

Fig 1 : Given input array (1-based indexing)

Fig 2 : Index denotes the each element i and corresponding values denotes the number of nested segments for this element.

Approach #1: For every element x, we will precompute the left and right position in a separate array in ~O(N). After this, we will traverse between the occurrences for each element x and calculate the number of nested segments by looping easily and this operation will cost ~O(N) in the worst case for every x, so for n elements the total time will be ~O(N²).

Is there something we can do better?

Approach #2: As discussed in approach 1, we will calculate the left and right position for element x but for the calculation of the nested segment, we will do it by segment trees.

We will build the segment tree over the 2n elements and mark 1 for each of the second occurrence of elements and build the sum-segment tree over this array and it will look something like below.

Fig 3 : Blue arrow indicates the left pointer and red arrow indicates the right pointer of each element x

We will only move into the left index of each element from the starting and we only need to calculate the number of red arrows(marked as 1) between left and right index of each element and before moving ahead we will do the point update at the right pointer of current element to 0,it ensures that this element will never be a part of answer for other elements as a nested segment. How?, because we are traversing from the start and we are accessing the left pointer in increasing fashion.

More clearly, let’s say i and j will be the left and right position of each element x. So, we will query for the range [i+1, j-1] to calculate the nested segments or say, the number of one present in the range [i+1, j-1] and before moving ahead, we will do the point update to the right pointer i.e, set(value at jth index to 0) and this type of operation we have already seen in the first problem and we don’t need to explain further how to do query and point update.

Let’s analyse the complexity of this problem:-

Query and point updates can be done in ~O(log N).So, overall complexity will be ~O(N log N) for N elements.

I think this gives you a broader idea about how the problem would break down into the query for range and point update. So that is it from my side about the segment trees, do practice and do let me know if you find this blog helpful or not and write in the comment section what topics i should cover in my upcoming blogs.

Till then stay safe and keep coding.