In this article, we will discuss how to use lazy propagation to solve a 2600 rating question on codeforces very easily….

Before diving further it is strongly recommended that you already have a basic idea about segment trees. If you want to read about segment trees you can check out this article.

Question :

( The problem link )

You are given a string s of length N. You would be given Q queries. Each query can be one of two types:

given l and r sort the substring s[1..r] in ascending order given l and r sort the substring s[l..r] in descending order.

After all the queries are performed you have to print the final string.

The size of the string is 1e6 and the number of queries is also 1e6 and the string contains only lower case alphabets.

Approach :

The brute force solution would be you sort the substring after each query. The complexity of this method would be O( Q*N*log(N) ).

You can use reduce its complexity even lower by using counting sort. The complexity would reduce to O( Q*N ). But this also doesn’t help either because our solution still runs in quadratic time.

We will try to solve this question by using segment trees. What happens when you sort the substring in an ascending manner ??? All the a’s in the substring will come to the front, then the b’s and so on till z.

So if we know the count of all the characters in a subarray, we can easily tell what the final substring will look like. For example, if you want to sort the subarray baeaecedb , after sorting the subarray would be 2 a, 2 b then 1 c , 1 d and 3 e i.e aabbcdeee.

To get the count of a character in a subarray we will build a segment tree. To be precise we will build 26 segment trees, one segment tree for each character, the query tree(char,l,r) will give how many characters are present in the subarray l to r.

Once a sorting query is asked, we can simply get the count of each character and update the subarray appropriately.

The pseudo-code would be something like this ( for now just assume that all query is of type 1, sort in ascending order ):

1. getting fequency and clearing the string for char 'a' to 'z':

freq[char] = query(char,l,r)

upd(char,l,r,0) 2. updating the substring for char 'a' to 'z':

upd(char,l,l+freq[char]-1,1)

l+=freq[char]

Now let's analyze the complexity of the above code. The query in a segment tree takes log(N) time whereas the normal brute update will take N*log(N) time. So total complexity is O(Q*(log(N)+ Nlog(N)), still quadratic because of the slow updates.

To reduce the time required to update, we will use a technique called lazy propagation. The concept is very simple, update only when it is required to update :).

Lazy Propagation:

To make range updates in the segment tree faster, we will update only the uppermost nodes that are affected in an update and won’t traverse further down the tree. These updates would be stored separately in a lazy node, and we will use these lazy nodes to accumulate the updates and then cumulatively perform updates on a segment not each time an update is asked.

The following diagram will make it more clear:

updating the range from 2 to 9

Suppose that you want to update the subarray 2 to 9, set all the characters to ‘a’. So we will traverse the segment tree for the character ‘a’ and update only the above-marked nodes. Note that here we are only updating 3 and traversing 6 nodes, not updating 8 nodes and traversing 17 nodes. These updates would be stored in the lazy nodes ( usually these nodes store information about how the subrange is currently affected, please see the implementation if it's still not clear ).

Now, let's make a query. We want to make calculate the number of ‘a’ in the range 3 to 7. while making the query we, will traverse and process any node which comes in our way and push the lazy updates to its children.

The red node represents that the nodes are updated, the green nodes represent that the nodes have some update remaining. Notice how the previous updates are being pushed down to the respective children.

Now the complexity of the update operation is the same as that of the query operation i.e O( log(N) ) and the overall complexity of our solution gets reduced to O(Q*( log(N) + log(N)) which will easily pass the time limit.

Implementation:

I strongly recommend that you have prior knowledge of the implementation of the normal segment tree.

The good thing about this is, the implementation of lazy propagation is almost similar to the normal segment tree, it’s just that you have to spend some time thinking about how to store and process the lazy updates. I will write the pseudo-code so that it becomes much more clearer :

1. building segtree

no change at all, same as normal segtree 2.updates

if current range lies completly inside the update range store the update in lazy node and return else if current range lies partially , go to its children

else return 2.query

if there are any updates on current (check the lazy node) the process and update the current range and push the update to its children if current range lies completly inside the query range return the answer else if current range lies partially , go to its children

else return

As you can see the only difference is the processing of the lazy nodes. We will write a separate function called pushdown to handle it. It would be something like this ( in here represents the char .. 0 for a and so on ):

The pushdown function

Now we slightly modify our update and query function

The update function. Use 1 for setting the range, use -1 for clearing the range

Our query function. You just need to add one line and everything remains the same

The build function would remain the same.

Conclusion:

We saw how lazy propagation can be implemented easily and can be used to solve very hard problems ( if you have a rating of 2600 on codeforces you are simply too good ).

My implementation. I suggest don’t look into the implementation part without giving a try yourself, because once you understand the concept it's just merely how you implement it.

There is another similar problem of rating 2600 which you could solve without changing much of the code.

Resources: