|
| 1 | +# LOJ-1164: Horrible Queries |
| 2 | + |
| 3 | +## Prerequisite: |
| 4 | +1. Segment Tree |
| 5 | +2. Range Operation: Lazy Propagation |
| 6 | + |
| 7 | +## Problem at a glance: |
| 8 | +In this problem we are required to perform two kinds of operation. |
| 9 | +1. Update a range by a given `value` |
| 10 | +2. Find the total sum of given range |
| 11 | + |
| 12 | +## Approach |
| 13 | +Its clear that we can use `cumulative sum` to answer the second type of query. However, updating a range of values using this approach would have a time complexity of **O(n)**, which could lead to a **"Time Limit Exceeded" (TLE)** error for larger inputs. If you are already familiar with **segment tree** then you can start with segment tree. But unlike segment tree we are required to **update range** which may lead to **TLE** To solve this problem we can use a variation of **segment tree** called **lazy propagation** |
| 14 | + |
| 15 | +## Lazy Propagation at a glance |
| 16 | +Lazy propagation is an optimization technique used with segment trees. If you are not familiar with segment trees, it is recommended to learn about them before proceeding. |
| 17 | + |
| 18 | +With lazy propagation, instead of updating a range instantly, we only mark the root node of that range for future updates. We maintain an extra array to keep track of which nodes need to be updated. Here's an illustration: |
| 19 | + |
| 20 | +<img src="lazyPropagationExample.jpg"> |
| 21 | + |
| 22 | +Let's say we want to update the range [4:6]. The root node of this range is the 3rd node. Rather than updating all the nodes below [4:6] immediately, we simply mark the immediate children of the 3rd node with a value that should be added in the future. Our second array tracks this information for each node to determine if its child nodes should be updated or not. When we need to query or update the range again, we will update the specific node. |
| 23 | + |
| 24 | +You will have a better understanding of this concept as you read through the tutorial. It's a straightforward problem if you can understand both the segment tree and lazy propagation algorithms. |
| 25 | + |
| 26 | +### Code |
| 27 | + |
| 28 | +``` |
| 29 | +#include<bits/stdc++.h> |
| 30 | +
|
| 31 | +using namespace std; |
| 32 | +
|
| 33 | +#define ll long long |
| 34 | +#define endl "\n" |
| 35 | +#define MAX 400010 |
| 36 | +
|
| 37 | +
|
| 38 | +ll segSum[MAX], lazy[MAX]; |
| 39 | +
|
| 40 | +void clearAll() { |
| 41 | + for (int i = 0; i < MAX; i++) { |
| 42 | + segSum[i] = 0; |
| 43 | + lazy[i] = 0; |
| 44 | + } |
| 45 | +} |
| 46 | +
|
| 47 | +
|
| 48 | +void update(int node, ll leftTree, ll rightTree, ll left, ll right, ll value) { |
| 49 | + int leftSide = node * 2; |
| 50 | + int rightSide = (node * 2) + 1; |
| 51 | + if (lazy[node]) { |
| 52 | + segSum[node] += ((rightTree - leftTree + 1) * lazy[node]); |
| 53 | + if (leftTree != rightTree) { |
| 54 | + lazy[leftSide] += lazy[node]; |
| 55 | + lazy[rightSide] += lazy[node]; |
| 56 | + } |
| 57 | + lazy[node] = 0; |
| 58 | + } |
| 59 | + if (leftTree > right || rightTree < left) { |
| 60 | + return; |
| 61 | + } |
| 62 | + if (leftTree >= left && rightTree <= right) { |
| 63 | + segSum[node] += ((rightTree - leftTree + 1) * value); |
| 64 | + if (leftTree != rightTree) { |
| 65 | + lazy[leftSide] += value; |
| 66 | + lazy[rightSide] += value; |
| 67 | + } |
| 68 | + return; |
| 69 | + } |
| 70 | + int mid = (leftTree + rightTree) >> 1; |
| 71 | + update(leftSide, leftTree, mid, left, right, value); |
| 72 | + update(rightSide, mid + 1, rightTree, left, right, value); |
| 73 | + segSum[node] = segSum[leftSide] + segSum[rightSide]; |
| 74 | +} |
| 75 | +
|
| 76 | +
|
| 77 | +ll query(int node, ll lt, ll rt, ll l, ll r) { |
| 78 | + int leftSide = node * 2; |
| 79 | + int rightSide = (node * 2) + 1; |
| 80 | + if (lazy[node]) { |
| 81 | + segSum[node] += ((rt - lt + 1) * lazy[node]); |
| 82 | + if (lt != rt) { |
| 83 | + lazy[leftSide] += lazy[node]; |
| 84 | + lazy[rightSide] += lazy[node]; |
| 85 | + } |
| 86 | + lazy[node] = 0; |
| 87 | + } |
| 88 | + if (lt > r || rt < l) { |
| 89 | + return 0; |
| 90 | + } |
| 91 | + if (l <= lt && rt <= r) { |
| 92 | + return segSum[node]; |
| 93 | + } |
| 94 | + int mid = (lt + rt) >> 1; |
| 95 | + ll leftSum = query(leftSide, lt, mid, l, r); |
| 96 | + ll rightSum = query(rightSide, mid + 1, rt, l, r); |
| 97 | + return leftSum + rightSum; |
| 98 | +} |
| 99 | +
|
| 100 | +
|
| 101 | +int main() { |
| 102 | + int t, cs = 1; |
| 103 | + cin >> t; |
| 104 | + while (t--) { |
| 105 | + int n,q; |
| 106 | + cin >> n >> q; |
| 107 | + clearAll(); |
| 108 | + cout <<"Case "<< cs++ << ":\n"; |
| 109 | + while(q--) { |
| 110 | + int choice, left, right; |
| 111 | + cin >> choice >> left >> right; |
| 112 | + if (choice == 0) { |
| 113 | + ll value; |
| 114 | + cin >> value; |
| 115 | + update(1, 0, n - 1, left, right, value); |
| 116 | + } |
| 117 | + else { |
| 118 | + cout << query(1, 0, n - 1, left, right) <<endl; |
| 119 | + } |
| 120 | + } |
| 121 | + } |
| 122 | + return 0; |
| 123 | +} |
| 124 | +``` |
| 125 | +## Resources: |
| 126 | +- [Segment Tree](https://cp-algorithms.com/data_structures/segment_tree.html) |
| 127 | +- [Lazy Propagation](https://www.topcoder.com/thrive/articles/range-operations-lazy-propagation) |
| 128 | + |
| 129 | +### Tutorial by: |
| 130 | +Profile Link: [Hasibur Rahman](https://lightoj.com/user/evan13) |
0 commit comments