Range Sum Queries with Update

Last Updated : 10 Sep, 2026

Let us consider the following problem to understand Segment Trees. We have an array arr[0 . . . n-1]. We should be able to 

  • Find the sum of elements from index l to r where 0 <= l <= r <= n-1
  • Change the value of a specified element of the array to a new value x. We need to do arr[i] = x where 0 <= i <= n-1.

Return an array containing the answers to all Type 1 queries in the same order as they appear in queries[][].

Input: arr[] = [1, 3, 5, 7, 9, 11], q = 3, queries = [[1, 0, 2], [2, 3, 17], [1, 0, 5]]
Output: [9, 46]
Explanation:
Query [1, 0, 2]: The sum of elements from index 0 to 2 is 1 + 3 + 5 = 9.
Query [2, 3, 17]: Update the value at index 3 from 7 to 17.
Query [1, 0, 5]: The sum of elements from index 0 to 5 is 1 + 3 + 5 + 17 + 9 + 11 = 46.

Input: arr[] = [2, 4, 6, 8], q = 5, queries[][] = [[1, 1, 3], [2, 2, 10], [1, 0, 2], [2, 0, 5], [1, 0, 3]]
Output: [18, 16, 27]
Explanation:
Query [1, 1, 3]: The sum of elements from index 1 to 3 is 4 + 6 + 8 = 18.
Query [2, 2, 10]: Update the value at index 2 from 6 to 10.
Query [1, 0, 2]: The sum of elements from index 0 to 2 is 2 + 4 + 10 = 16.
Query [2, 0, 5]: Update the value at index 0 from 2 to 5.
Query [1, 0, 3]: The sum of elements from index 0 to 3 is 5 + 4 + 10 + 8 = 27.

Try It Yourself
redirect icon

Using Nested Loop - O(n) Time for Query and O(1) for Update

A simple solution is to run a loop from l to r and calculate the sum of elements in the given range.

To update a value, simply do arr[i] = x.

Using Prefix Sum - O(1) Time for Query and O(n) for Update

Another solution is to create another array and store the sum from start to i ,at the ith index in this array.

The sum of a given range can now be calculated in O(1) time, but update operation takes O(n) time now.

This works well if the number of query operations is large and very few updates.

Using Segment Tree - O(Log n) Time for Query and O(Log n) for Update

The most efficient way is to use a segment tree, we can use a Segment Tree to do both operations in O(log(n)) time.

Representation of Segment trees 

  • Leaf Nodes are the elements of the input array. 
  • Each internal node represents some merging of the leaf nodes. The merging may be different for different problems. For this problem, merging is sum of leaf nodes under a node.
  • An array representation of tree is used to represent Segment Trees. For each node at index i, the left child is at index (2 * i + 1), right child at (2 * i + 2) and the parent is at  (⌊(i - 1) / 2⌋).

Construction of Segment Tree from the given array:

We start with a segment arr[0 . . . n-1]. and every time we divide the current segment into two (if it has not yet become a segment of length 1), and then call the same procedure on both halves, and for each such segment, we store the sum in the corresponding node.

All levels of the constructed segment tree will be completely filled except the last level. Also, the tree will be a Full Binary Tree because we always divide segment in two, at every level.

Since the constructed tree is always a full binary tree with n leaves, there will be n-1 internal nodes. So the total number of nodes will be 2 * n - 1.

What is the height of the segment tree for a given array:

Height of the segment tree will be ⌈log₂(n)⌉. Since the tree is represented using array and relation between parent and child indexes must be maintained, size of memory allocated for segment tree will be (2 * 2⌈log2n⌉  - 1).

However, in practice, size of memory allocated for segment tree implementations are 4 * n, which provides sufficient space for the tree without requiring the exact size calculation.

Query for Sum of a given range:

Once the tree is constructed, how to get the sum using the constructed segment tree. The following is the algorithm to get the sum of elements.  

int getSum(node, l, r)
{
if the range of the node is completely within [l, r]
return value in the node
else if the range of the node is completely outside [l, r]
return 0
else
return getSum(node's left child, l, r) + getSum(node's right child, l, r)
}

In the above implementation, there are three cases we need to take into consideration

  • If the range of the current node while traversing the tree is not in the given range then did not add the value of that node in ans.
  • If the range of node is partially overlapped with the given range then move either left or right according to the overlapping.
  • If the range is completely overlapped by the given range then add it to the ans.

Update a value: 

update(node, l, r, index, value)
{
if (l == r) {
node's value = value
return
}

mid = (l + r) / 2

if (index <= mid) {
update(node's left child, l, mid, index, value)
} else {
update(node's right child, mid + 1, r, index, value)
}

node's value = left child's value + right child's value
}

  • If the current node represents a segment containing only the target index, update the value of that node with the new value.
  • Otherwise, find the midpoint of the current segment.
  • If the target index lies in the left half, recursively update the left child.
  • Otherwise, recursively update the right child.
  • After updating the child, recompute the current node's value as the sum of its left and right child values.

The algorithmic steps to implement a segment tree are:

  • Initialize the segment tree with a size of 4 * n, where n is the number of elements in the array.
  • buildTree() function:
    In the buildTree function, if the left and right bounds of the current segment are equal, the segment contains a single element. Set the value of the current node to the corresponding element of the array.
  • Otherwise, calculate the midpoint of the current segment and recursively build the left and right subsegments. After that, combine the values of both children and store the result in the current node.
  • query() function:
    In the query function, if the current segment is completely outside the query range, return the appropriate identity value, such as 0 for a sum query.
  • If the current segment is completely inside the query range, return the value stored in the current node.
  • Otherwise, the segment partially overlaps with the query range. Calculate the midpoint, recursively query both children, and combine their results using the required operation, such as sum, minimum, or maximum.
  • The query function can then be called with the left and right bounds of the desired range to obtain the required result.

Note: The implementation details, such as the type of aggregation and the way the midpoint is calculated, can vary based on the specific use case.

C++
#include <bits/stdc++.h>
using namespace std;

// Stores the segment tree.
vector<int> seg;

// Stores the size of the array.
int n;

// Builds the segment tree.
void build(int idx, int low, int high, vector<int> &arr)
{
    // If the segment contains one element,
    // store that element in the current node.
    if (low == high)
    {
        seg[idx] = arr[low];
        return;
    }

    // Find the midpoint of the current segment.
    int mid = (low + high) / 2;

    // Build the left subtree.
    build(2 * idx + 1, low, mid, arr);

    // Build the right subtree.
    build(2 * idx + 2, mid + 1, high, arr);

    // Store the sum of the left and right children.
    seg[idx] = seg[2 * idx + 1] + seg[2 * idx + 2];
}

// Returns the sum of elements in the range [l, r].
int query(int idx, int low, int high, int l, int r)
{
    // If the current segment is completely outside
    // the query range, return 0.
    if (r < low || high < l)
        return 0;

    // If the current segment is completely inside
    // the query range, return its stored sum.
    if (l <= low && high <= r)
        return seg[idx];

    // Find the midpoint.
    int mid = (low + high) / 2;

    // Query both children and add their results.
    return query(2 * idx + 1, low, mid, l, r) + query(2 * idx + 2, mid + 1, high, l, r);
}

// Updates the value at index pos to val.
void update(int idx, int low, int high, int pos, int val)
{
    // If the current segment contains only the target index,
    // update its value.
    if (low == high)
    {
        seg[idx] = val;
        return;
    }

    // Find the midpoint.
    int mid = (low + high) / 2;

    // If the target index lies in the left half,
    // update the left child.
    if (pos <= mid)
        update(2 * idx + 1, low, mid, pos, val);
    else
        // Otherwise, update the right child.
        update(2 * idx + 2, mid + 1, high, pos, val);

    // Recalculate the current node after the update.
    seg[idx] = seg[2 * idx + 1] + seg[2 * idx + 2];
}

// Processes all range sum queries and updates.
vector<int> rangeSumQueries(vector<int> &arr, vector<vector<int>> &queries)
{
    n = arr.size();

    // Allocate sufficient space for the segment tree.
    seg.assign(4 * n, 0);

    // Build the segment tree.
    build(0, 0, n - 1, arr);

    // Stores answers for type 1 queries.
    vector<int> ans;

    // Process every query.
    for (auto &q : queries)
    {
        // Type 1: Find the sum in range [q[1], q[2]].
        if (q[0] == 1)
        {
            ans.push_back(query(0, 0, n - 1, q[1], q[2]));
        }
        // Type 2: Update arr[q[1]] to q[2].
        else
        {
            update(0, 0, n - 1, q[1], q[2]);
        }
    }

    return ans;
}

int main()
{
    vector<int> arr = {1, 3, 5, 7, 9, 11};

    // Type 1: [1, l, r] -> range sum query.
    // Type 2: [2, index, value] -> point update.
    vector<vector<int>> queries = {{1, 0, 2}, {2, 3, 17}, {1, 0, 5}};

    vector<int> ans = rangeSumQueries(arr, queries);

    for (int x : ans)
        cout << x << " ";

    return 0;
}
Java
import java.util.*;

class GFG {
    // Stores the segment tree.
    static int[] seg;

    // Stores the size of the array.
    static int n;

    // Builds the segment tree.
    static void build(int idx, int low, int high, int[] arr)
    {
        // If the segment contains one element,
        // store that element in the current node.
        if (low == high) {
            seg[idx] = arr[low];
            return;
        }

        // Find the midpoint of the current segment.
        int mid = (low + high) / 2;

        // Build the left subtree.
        build(2 * idx + 1, low, mid, arr);

        // Build the right subtree.
        build(2 * idx + 2, mid + 1, high, arr);

        // Store the sum of the left and right children.
        seg[idx] = seg[2 * idx + 1] + seg[2 * idx + 2];
    }

    // Returns the sum of elements in the range [l, r].
    static int query(int idx, int low, int high, int l,
                     int r)
    {
        // If the current segment is completely outside
        // the query range, return 0.
        if (r < low || high < l)
            return 0;

        // If the current segment is completely inside
        // the query range, return its stored sum.
        if (l <= low && high <= r)
            return seg[idx];

        // Find the midpoint.
        int mid = (low + high) / 2;

        // Query both children and add their results.
        return query(2 * idx + 1, low, mid, l, r)
            + query(2 * idx + 2, mid + 1, high, l, r);
    }

    // Updates the value at index pos to val.
    static void update(int idx, int low, int high, int pos,
                       int val)
    {
        // If the current segment contains only the target
        // index, update its value.
        if (low == high) {
            seg[idx] = val;
            return;
        }

        // Find the midpoint.
        int mid = (low + high) / 2;

        // If the target index lies in the left half,
        // update the left child.
        if (pos <= mid)
            update(2 * idx + 1, low, mid, pos, val);
        else
            // Otherwise, update the right child.
            update(2 * idx + 2, mid + 1, high, pos, val);

        // Recalculate the current node after the update.
        seg[idx] = seg[2 * idx + 1] + seg[2 * idx + 2];
    }

    // Processes all range sum queries and updates.
    static ArrayList<Integer>
    rangeSumQueries(int[] arr, int[][] queries)
    {
        n = arr.length;

        // Allocate sufficient space for the segment tree.
        seg = new int[4 * n];

        // Build the segment tree.
        build(0, 0, n - 1, arr);

        // Stores answers for type 1 queries.
        ArrayList<Integer> ans = new ArrayList<>();

        // Process every query.
        for (int[] q : queries) {
            // Type 1: Find the sum in range [q[1], q[2]].
            if (q[0] == 1) {
                ans.add(query(0, 0, n - 1, q[1], q[2]));
            }
            // Type 2: Update arr[q[1]] to q[2].
            else {
                update(0, 0, n - 1, q[1], q[2]);
            }
        }

        return ans;
    }

    public static void main(String[] args)
    {
        int[] arr = { 1, 3, 5, 7, 9, 11 };

        // Type 1: [1, l, r] -> range sum query.
        // Type 2: [2, index, value] -> point update.
        int[][] queries
            = { { 1, 0, 2 }, { 2, 3, 17 }, { 1, 0, 5 } };

        ArrayList<Integer> ans
            = rangeSumQueries(arr, queries);

        for (int x : ans)
            System.out.print(x + " ");
    }
}
Python
# Stores the segment tree.
seg = []

# Stores the size of the array.
n = 0

# Builds the segment tree.
def build(idx, low, high, arr):

    # If the segment contains one element,
    # store that element in the current node.
    if low == high:
        seg[idx] = arr[low]
        return

    # Find the midpoint of the current segment.
    mid = (low + high) // 2

    # Build the left subtree.
    build(2 * idx + 1, low, mid, arr)

    # Build the right subtree.
    build(2 * idx + 2, mid + 1, high, arr)

    # Store the sum of the left and right children.
    seg[idx] = seg[2 * idx + 1] + seg[2 * idx + 2]


# Returns the sum of elements in the range [l, r].
def query(idx, low, high, l, r):

    # If the current segment is completely outside
    # the query range, return 0.
    if r < low or high < l:
        return 0

    # If the current segment is completely inside
    # the query range, return its stored sum.
    if l <= low and high <= r:
        return seg[idx]

    # Find the midpoint.
    mid = (low + high) // 2

    # Query both children and add their results.
    return (query(2 * idx + 1, low, mid, l, r) +
            query(2 * idx + 2, mid + 1, high, l, r))


# Updates the value at index pos to val.
def update(idx, low, high, pos, val):

    # If the current segment contains only the target index,
    # update its value.
    if low == high:
        seg[idx] = val
        return

    # Find the midpoint.
    mid = (low + high) // 2

    # If the target index lies in the left half,
    # update the left child.
    if pos <= mid:
        update(2 * idx + 1, low, mid, pos, val)
    else:
        # Otherwise, update the right child.
        update(2 * idx + 2, mid + 1, high, pos, val)

    # Recalculate the current node after the update.
    seg[idx] = seg[2 * idx + 1] + seg[2 * idx + 2]


# Processes all range sum queries and updates.
def rangeSumQueries(arr, queries):
    global seg, n

    n = len(arr)

    # Allocate sufficient space for the segment tree.
    seg = [0] * (4 * n)

    # Build the segment tree.
    build(0, 0, n - 1, arr)

    # Stores answers for type 1 queries.
    ans = []

    # Process every query.
    for q in queries:

        # Type 1: Find the sum in range [q[1], q[2]].
        if q[0] == 1:
            ans.append(query(0, 0, n - 1, q[1], q[2]))

        # Type 2: Update arr[q[1]] to q[2].
        else:
            update(0, 0, n - 1, q[1], q[2])

    return ans

# Driver Code
if __name__ == "__main__":
    arr = [1, 3, 5, 7, 9, 11]

    # Type 1: [1, l, r] -> range sum query.
    # Type 2: [2, index, value] -> point update.
    queries = [
        [1, 0, 2],
        [2, 3, 17],
        [1, 0, 5]
    ]

    ans = rangeSumQueries(arr, queries)

    for x in ans:
        print(x, end=" ")
C#
using System;
using System.Collections.Generic;

class GFG {
    // Stores the segment tree.
    static int[] seg;

    // Stores the size of the array.
    static int n;

    // Builds the segment tree.
    static void build(int idx, int low, int high, int[] arr)
    {
        // If the segment contains one element,
        // store that element in the current node.
        if (low == high) {
            seg[idx] = arr[low];
            return;
        }

        // Find the midpoint of the current segment.
        int mid = (low + high) / 2;

        // Build the left subtree.
        build(2 * idx + 1, low, mid, arr);

        // Build the right subtree.
        build(2 * idx + 2, mid + 1, high, arr);

        // Store the sum of the left and right children.
        seg[idx] = seg[2 * idx + 1] + seg[2 * idx + 2];
    }

    // Returns the sum of elements in the range [l, r].
    static int query(int idx, int low, int high, int l,
                     int r)
    {
        // If the current segment is completely outside
        // the query range, return 0.
        if (r < low || high < l)
            return 0;

        // If the current segment is completely inside
        // the query range, return its stored sum.
        if (l <= low && high <= r)
            return seg[idx];

        // Find the midpoint.
        int mid = (low + high) / 2;

        // Query both children and add their results.
        return query(2 * idx + 1, low, mid, l, r)
            + query(2 * idx + 2, mid + 1, high, l, r);
    }

    // Updates the value at index pos to val.
    static void update(int idx, int low, int high, int pos,
                       int val)
    {
        // If the current segment contains only the target
        // index, update its value.
        if (low == high) {
            seg[idx] = val;
            return;
        }

        // Find the midpoint.
        int mid = (low + high) / 2;

        // If the target index lies in the left half,
        // update the left child.
        if (pos <= mid)
            update(2 * idx + 1, low, mid, pos, val);
        else
            // Otherwise, update the right child.
            update(2 * idx + 2, mid + 1, high, pos, val);

        // Recalculate the current node after the update.
        seg[idx] = seg[2 * idx + 1] + seg[2 * idx + 2];
    }

    // Processes all range sum queries and updates.
    static List<int> rangeSumQueries(int[] arr,
                                     int[, ] queries)
    {
        n = arr.Length;

        // Allocate sufficient space for the segment tree.
        seg = new int[4 * n];

        // Build the segment tree.
        build(0, 0, n - 1, arr);

        // Stores answers for type 1 queries.
        List<int> ans = new List<int>();

        // Process every query.
        for (int i = 0; i < queries.GetLength(0); i++) {
            // Type 1: Find the sum in range [q[1], q[2]].
            if (queries[i, 0] == 1) {
                ans.Add(query(0, 0, n - 1, queries[i, 1],
                              queries[i, 2]));
            }
            // Type 2: Update arr[q[1]] to q[2].
            else {
                update(0, 0, n - 1, queries[i, 1],
                       queries[i, 2]);
            }
        }

        return ans;
    }

    public static void Main()
    {
        int[] arr = { 1, 3, 5, 7, 9, 11 };

        // Type 1: [1, l, r] -> range sum query.
        // Type 2: [2, index, value] -> point update.
        int[, ] queries
            = { { 1, 0, 2 }, { 2, 3, 17 }, { 1, 0, 5 } };

        List<int> ans = rangeSumQueries(arr, queries);

        foreach(int x in ans) Console.Write(x + " ");
    }
}
JavaScript
// Builds the segment tree.
function build(idx, low, high, arr, seg)
{
    // If the segment contains one element,
    // store that element in the current node.
    if (low === high) {
        seg[idx] = arr[low];
        return;
    }

    // Find the midpoint of the current segment.
    let mid = Math.floor((low + high) / 2);

    // Build the left subtree.
    build(2 * idx + 1, low, mid, arr, seg);

    // Build the right subtree.
    build(2 * idx + 2, mid + 1, high, arr, seg);

    // Store the sum of the left and right children.
    seg[idx] = seg[2 * idx + 1] + seg[2 * idx + 2];
}

// Returns the sum of elements in the range [l, r].
function query(idx, low, high, l, r, seg)
{
    // If the current segment is completely outside
    // the query range, return 0.
    if (r < low || high < l)
        return 0;

    // If the current segment is completely inside
    // the query range, return its stored sum.
    if (l <= low && high <= r)
        return seg[idx];

    // Find the midpoint.
    let mid = Math.floor((low + high) / 2);

    // Query both children and add their results.
    return query(2 * idx + 1, low, mid, l, r, seg)
           + query(2 * idx + 2, mid + 1, high, l, r, seg);
}

// Updates the value at index pos to val.
function update(idx, low, high, pos, val, seg)
{
    // If the current segment contains only the target
    // index, update its value.
    if (low === high) {
        seg[idx] = val;
        return;
    }

    // Find the midpoint.
    let mid = Math.floor((low + high) / 2);

    // If the target index lies in the left half,
    // update the left child.
    if (pos <= mid)
        update(2 * idx + 1, low, mid, pos, val, seg);
    else
        // Otherwise, update the right child.
        update(2 * idx + 2, mid + 1, high, pos, val, seg);

    // Recalculate the current node after the update.
    seg[idx] = seg[2 * idx + 1] + seg[2 * idx + 2];
}

// Processes all range sum queries and updates.
function rangeSumQueries(arr, queries)
{
    let n = arr.length;

    // Allocate sufficient space for the segment tree.
    let seg = new Array(4 * n).fill(0);

    // Build the segment tree.
    build(0, 0, n - 1, arr, seg);

    // Stores answers for type 1 queries.
    let ans = [];

    // Process every query.
    for (let q of queries) {
        // Type 1: Find the sum in range [q[1], q[2]].
        if (q[0] === 1) {
            ans.push(query(0, 0, n - 1, q[1], q[2], seg));
        }
        // Type 2: Update arr[q[1]] to q[2].
        else {
            update(0, 0, n - 1, q[1], q[2], seg);
        }
    }

    return ans;
}

// Driver Code
let arr = [ 1, 3, 5, 7, 9, 11 ];

// Type 1: [1, l, r] -> range sum query.
// Type 2: [2, index, value] -> point update.
let queries = [ [ 1, 0, 2 ], [ 2, 3, 17 ], [ 1, 0, 5 ] ];

let ans = rangeSumQueries(arr, queries);

let res = "";
for (let x of ans)
    res += x + " ";

console.log(res.trim());

Output
9 46 

Time complexity: O(n + q * log(n)) 
Auxiliary Space: O(n)

Comment