Prerequisites: Segment Tree
Given a number N which represents the size of the array initialized to 0 and Q queries to process where there are two types of queries:
- 1 P V: Put the value V at position P.
- 2 L R: Output the sum of values from L to R.
The task is to answer these queries.
Constraints:
- 1 ? N ? 1018
- Q ? 105
- 1 ? L ? R? N
Note: Queries are online. Therefore:
- L = (previousAnswer + L) % N + 1
- R = (previousAnswer + R) % N + 1
Examples:
Input: N = 5, Q = 5, arr[][] = {{1, 2, 3}, {1, 1, 4}, {1, 3, 5}, {1, 4, 7}, {2, 3, 4}}
Output: 12
Explanation:
There are five queries. Since N = 5, therefore, initially, the array is {0, 0, 0, 0, 0}
For query 1: 1 2 3 array = {0, 3, 0, 0, 0}
For query 2: 1 1 4 array = {4, 3, 0, 0, 0}
For query 3: 1 3 5 array = {4, 3, 5, 0, 0}
For query 4: 1 4 7 array = {4, 3, 5, 7, 0}
For query 5: 2 3 4 Sum from [3, 4] = 7 + 5 = 12.
Input: N = 3, Q = 2, arr[][] = {{1, 1, 1}, {1, 2, 2}, {1, 3, 3}}
Output: 0
Approach: Here, since the updates are high, Kadane’s algorithm doesn’t work quite well. Moreover, since it is given that the queries are online, a simple segment tree would not be able to solve this problem because the constraints for the number of elements is very high. Therefore, a new type of data structure, a dynamic segment tree is used in this problem.
Dynamic Segment Tree: Dynamic segment tree is not a new data structure. It is very similar to the segment tree. The following are the properties of the dynamic segment tree:
- Instead of using an array to represent the intervals, a node is created whenever a new interval is to be updated.
- The following is the structure of the node of the dynamic segment tree:
C++
struct Node {
long long value;
struct Node *L, *R;
};
struct Node* getnode()
{
struct Node* temp = new struct Node;
temp->value = 0;
temp->L = NULL;
temp->R = NULL;
return temp;
}
|
Java
static class Node {
int value;
Node L, R;
}
static Node getnode() {
Node temp = new Node();
temp.value = 0 ;
temp.L = null ;
temp.R = null ;
return temp;
}
|
Python3
class Node:
def __init__( self ):
self .value = 0
self .L = None
self .R = None
def getnode():
temp = Node()
temp.value = 0
temp.L = None
temp.R = None
return temp
|
C#
using System;
public class Node
{
public int value;
public Node L, R;
}
public class Program
{
public static Node getnode()
{
Node temp = new Node();
temp.value = 0;
temp.L = null ;
temp.R = null ;
return temp;
}
}
|
Javascript
class Node {
constructor() {
this .value = 0;
this .L = null ;
this .R = null ;
}
}
function getnode() {
let temp = new Node();
temp.value = 0;
temp.L = null ;
temp.R = null ;
return temp;
}
|
- Clearly, the above structure is the same as a Binary Search Tree. In every node, we are storing the node’s value and two pointers pointing to the left and right subtree.
- The Interval of the root is from [1, N], the interval of the left subtree will be [1, N/2] and the interval for the right subtree will be [N/2 + 1, N].
- Similarly, for every node, we can calculate the interval it is representing. Let’s say the interval of the current node is [L, R]. Then, the Interval of its left and right subtree are [L, (L + R)/2] and [(L + R)/2+1, R] respectively.
- Since we are creating a new node only when required, the build() function from the segment tree is completely removed.
Before getting into the algorithm for the operations, let’s define the terms used in this article:
- Node’s interval: It is the interval the node is representing.
- Required interval: Interval for which the sum is to calculate.
- Required index: Index at which Update is required.
The following is the algorithm used for the operations on the tree with the above-mentioned properties:
- Point Update: The following algorithm is used for the point update:
- Start with the root node.
- If the interval at the node doesn’t overlap with the required index then return.
- If the node is a NULL entry then create a new node with the appropriate intervals and descend into that node by going back to step 2 for every new child created.
- If both, intervals and the index at which the value is to be stored are equal, then store the value into at that node.
- If the interval at the node overlaps partially with the required index then descend into its children and continue the execution from step 2.
- Finding the sum for every query: The following algorithm is used to find the sum for every query:
- Start with the root node.
- If the node is a NULL or the interval at that node doesn’t overlap with the required interval, then return 0.
- If the interval at the node completely overlaps with the required interval then return the value stored at the node.
- If the interval at the node overlaps partially with the required interval then descend into its children and continue the execution from step 2 for both of its children.
Example: Lets visualize the update and sum with an example. Let N = 10 and the operations needed to perform on the tree are as follows:
- Insert 10 at position 1.
- Find the sum of value of indices from 2 to 8.
- Insert 3 at position 5.
- Find the sum of value of indices from 3 to 6.
- Initially, for the value N = 10, the tree is empty. Therefore:

- Insert 10 at position 1. In order to do this, create a new node until we get the required interval. Therefore:

- Find the sum of value of indices from 2 to 8. In order to do this, the sum from [1, 8] is found and the value [1, 2] is subtracted from it. Since the node [1, 8] is not yet created, the value of [1, 8] is the value of the root [1, 10]. Therefore:

- Insert 3 at position 5. In order to do this, create a new node until we get the required interval. Therefore:

Below is the implementation of the above approach:
C++
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct Node {
ll value;
struct Node *L, *R;
};
struct Node* getnode()
{
struct Node* temp = new struct Node;
temp->value = 0;
temp->L = NULL;
temp->R = NULL;
return temp;
}
struct Node* root;
void UpdateHelper( struct Node* curr, ll index,
ll L, ll R, ll val)
{
if (L > index || R < index)
return ;
if (L == R && L == index) {
curr->value = val;
return ;
}
ll mid = L - (L - R) / 2;
ll sum1 = 0, sum2 = 0;
if (index <= mid) {
if (curr->L == NULL)
curr->L = getnode();
UpdateHelper(curr->L, index, L, mid, val);
}
else {
if (curr->R == NULL)
curr->R = getnode();
UpdateHelper(curr->R, index, mid + 1, R, val);
}
if (curr->L)
sum1 = curr->L->value;
if (curr->R)
sum2 = curr->R->value;
curr->value = sum1 + sum2;
return ;
}
ll queryHelper( struct Node* curr, ll a,
ll b, ll L, ll R)
{
if (curr == NULL)
return 0;
if (L > b || R < a)
return 0;
if (L >= a && R <= b)
return curr->value;
ll mid = L - (L - R) / 2;
return queryHelper(curr->L, a, b, L, mid)
+ queryHelper(curr->R, a, b, mid + 1, R);
}
ll query( int L, int R)
{
return queryHelper(root, L, R, 1, 10);
}
void update( int index, int value)
{
UpdateHelper(root, index, 1, 10, value);
}
void operations()
{
root = getnode();
update(1, 10);
update(3, 5);
cout << query(2, 8) << endl;
cout << query(1, 10) << endl;
}
int main()
{
operations();
return 0;
}
|
Java
class GFG {
static class Node {
int value;
Node L, R;
}
static Node getnode() {
Node temp = new Node();
temp.value = 0 ;
temp.L = null ;
temp.R = null ;
return temp;
}
static Node root = new Node();
static void UpdateHelper(Node curr, int index, int L, int R, int val) {
if (L > index || R < index)
return ;
if (L == R && L == index) {
curr.value = val;
return ;
}
int mid = L - (L - R) / 2 ;
int sum1 = 0 , sum2 = 0 ;
if (index <= mid) {
if (curr.L == null )
curr.L = getnode();
UpdateHelper(curr.L, index, L, mid, val);
}
else {
if (curr.R == null )
curr.R = getnode();
UpdateHelper(curr.R, index, mid + 1 , R, val);
}
if (curr.L != null )
sum1 = curr.L.value;
if (curr.R != null )
sum2 = curr.R.value;
curr.value = sum1 + sum2;
return ;
}
static int queryHelper(Node curr, int a, int b, int L, int R) {
if (curr == null )
return 0 ;
if (L > b || R < a)
return 0 ;
if (L >= a && R <= b)
return curr.value;
int mid = L - (L - R) / 2 ;
return queryHelper(curr.L, a, b, L, mid) + queryHelper(curr.R, a, b, mid + 1 , R);
}
static int query( int L, int R) {
return queryHelper(root, L, R, 1 , 10 );
}
static void update( int index, int value) {
UpdateHelper(root, index, 1 , 10 , value);
}
static void operations() {
root = getnode();
update( 1 , 10 );
update( 3 , 5 );
System.out.println(query( 2 , 8 ));
System.out.println(query( 1 , 10 ));
}
public static void main(String[] args) {
operations();
}
}
|
Python3
class Node:
def __init__( self ):
self .value = - 1
self .L, self .R = None , None
def getnode():
temp = Node()
temp.value = 0
temp.L = None
temp.R = None
return temp
root = None
def UpdateHelper(curr, index, L, R, val):
if (L > index or R < index):
return
if (L = = R and L = = index) :
curr.value = val
return
mid = int (L - (L - R) / 2 )
sum1 = 0 ; sum2 = 0
if (index < = mid) :
if (curr.L = = None ):
curr.L = getnode()
UpdateHelper(curr.L, index, L, mid, val)
else :
if (curr.R = = None ):
curr.R = getnode()
UpdateHelper(curr.R, index, mid + 1 , R, val)
if (curr.L):
sum1 = curr.L.value
if (curr.R):
sum2 = curr.R.value
curr.value = sum1 + sum2
return
def queryHelper(curr, a, b, L, R):
if (curr = = None ):
return 0
if (L > b or R < a):
return 0
if (L > = a and R < = b):
return curr.value
mid = int (L - (L - R) / 2 )
return queryHelper(curr.L, a, b, L, mid) + queryHelper(curr.R, a, b, mid + 1 , R)
def query(L, R):
return queryHelper(root, L, R, 1 , 10 )
def update(index, value):
UpdateHelper(root, index, 1 , 10 , value)
def operations():
global root
root = getnode()
update( 1 , 10 )
update( 3 , 5 )
print (query( 2 , 8 ))
print (query( 1 , 10 ))
if __name__ = = '__main__' :
operations()
|
C#
using System;
class GFG {
public class Node {
public int value;
public Node L, R;
}
static Node getnode() {
Node temp = new Node();
temp.value = 0;
temp.L = null ;
temp.R = null ;
return temp;
}
static Node root = new Node();
static void UpdateHelper(Node curr, int index, int L, int R, int val) {
if (L > index || R < index)
return ;
if (L == R && L == index) {
curr.value = val;
return ;
}
int mid = L - (L - R) / 2;
int sum1 = 0, sum2 = 0;
if (index <= mid) {
if (curr.L == null )
curr.L = getnode();
UpdateHelper(curr.L, index, L, mid, val);
}
else {
if (curr.R == null )
curr.R = getnode();
UpdateHelper(curr.R, index, mid + 1, R, val);
}
if (curr.L != null )
sum1 = curr.L.value;
if (curr.R != null )
sum2 = curr.R.value;
curr.value = sum1 + sum2;
return ;
}
static int queryHelper(Node curr, int a, int b, int L, int R) {
if (curr == null )
return 0;
if (L > b || R < a)
return 0;
if (L >= a && R <= b)
return curr.value;
int mid = L - (L - R) / 2;
return queryHelper(curr.L, a, b, L, mid) + queryHelper(curr.R, a, b, mid + 1, R);
}
static int query( int L, int R) {
return queryHelper(root, L, R, 1, 10);
}
static void update( int index, int value) {
UpdateHelper(root, index, 1, 10, value);
}
static void operations() {
root = getnode();
update(1, 10);
update(3, 5);
Console.WriteLine(query(2, 8));
Console.WriteLine(query(1, 10));
}
public static void Main(String[] args) {
operations();
}
}
|
Javascript
<script>
class Node
{
constructor()
{
this .L = null ;
this .R = null ;
this .value = 0;
}
}
function getnode()
{
let temp = new Node();
return temp;
}
let root = new Node();
function UpdateHelper(curr, index, L, R, val)
{
if (L > index || R < index)
return ;
if (L == R && L == index)
{
curr.value = val;
return ;
}
let mid = L - parseInt((L - R) / 2, 10);
let sum1 = 0, sum2 = 0;
if (index <= mid)
{
if (curr.L == null )
curr.L = getnode();
UpdateHelper(curr.L, index, L, mid, val);
}
else
{
if (curr.R == null )
curr.R = getnode();
UpdateHelper(curr.R, index, mid + 1, R, val);
}
if (curr.L != null )
sum1 = curr.L.value;
if (curr.R != null )
sum2 = curr.R.value;
curr.value = sum1 + sum2;
return ;
}
function queryHelper(curr, a, b, L, R)
{
if (curr == null )
return 0;
if (L > b || R < a)
return 0;
if (L >= a && R <= b)
return curr.value;
let mid = L - parseInt((L - R) / 2, 10);
return queryHelper(curr.L, a, b, L, mid) +
queryHelper(curr.R, a, b, mid + 1, R);
}
function query(L, R)
{
return queryHelper(root, L, R, 1, 10);
}
function update(index, value)
{
UpdateHelper(root, index, 1, 10, value);
}
function operations()
{
root = getnode();
update(1, 10);
update(3, 5);
document.write(query(2, 8) + "</br>" );
document.write(query(1, 10) + "</br>" );
}
operations();
</script>
|
Time Complexity: O(Q * logN)
Auxiliary Space: O(N)
Related Topic: Segment Tree