Perform append, update, delete and range sum queries on the given array

Given an array arr[] of size N and the task is to answer Q queries of the following types:

  • 1 X 0: Append X at the back of array.
  • 2 X Y: Set arr[X] = Y.
  • 3 X 0: Delete arr[X].
  • 4 X Y: Find the sum in the range [X, Y].

Note that after deleting arr[X] in query type 3, all the elements after index X will be shifted left by one position.

Examples:

Input: arr[] = {1, 2, 5, 3, 4}, Q[][] = {
{4, 2, 4},
{3, 3, 0},
{1, 6, 0},
{4, 3, 5}}
Output:
10
13

First Query -> sum(arr[1], arr[2], arr[3])
Second Query -> arr[] = { 1, 2, 3, 4 }
Third Query -> arr[] = { 1, 2, 3, 4, 6 }
Fourth Query -> sum(arr[2], arr[3], arr[4])



Input: arr[] = {1, 2, 3, 4, 5}, Q[][] = {
{4, 1, 5},
{3, 3, 0},
{1, 6, 0},
{4, 3, 5},
{2, 4, 10}.
{4, 1, 5}}
Output:
15
15
23

Naive approach: The naive way to solve this problem is to execute the queries directly on the given array which will have a time complexity of O(Q * N).

Efficient approach:

  • As there are some data structures like Segment tree or BIT(Fenwick Tree) that provide the point update and range sum in O(logN) per queries.
  • There post uses a Fenwick Tree to do the same, so it is highly recommended to go through point update in Fenwick Tree.
  • But the main problem is to perform the delete operation ( type-3 queries ), after performing there is a need of shifting, that will again lead to O(Q * N) in the worst case.
  • A trick can be used that after deleting the element, just update the value at A[X] = 0 and map the index after X to X + number of elements deleted before X.
  • To find the number of elements deleted before X, there will be another fenwick tree ( as idx in the implementation ) used and keep adding 1 at that index where the delete operation is being performed.
  • Means at the time of fetching or getting a particular index, a query can be made to the idx tree and update index X to X + sum(X, idx).

Below is the implementation of the above approach:

C++

filter_none

edit
close

play_arrow

link
brightness_4
code

// C++ implementation of the approach
#include <bits/stdc++.h>
using namespace std;
  
// Size of the array (MAX)
const int N = 2e5 + 10;
  
// To store the sum of
// the array elements
vector<int> bit(N, 0);
  
// To keep track of how many type 3
// queries have been performed before
// a particular index
vector<int> idx(N, 0);
  
// Function to perform the point
// update in Fenwick tree
void update(int idx, int val,
            vector<int>& bitt)
{
    while (idx < N) {
        bitt[idx] += val;
        idx += idx & (-idx);
    }
}
  
// Function to return the sum
// of the elements A[1...idx]
// from BIT
int sum(int idx,
        vector<int>& bitt)
{
  
    int res = 0;
    while (idx > 0) {
        res += bitt[idx];
        idx -= idx & (-idx);
    }
  
    return res;
}
  
// Function to perform the queries and
// return the answer vector
vector<int> peformQueries(vector<int>& A,
                          vector<vector<int> >& B)
{
  
    // For 1 bases indexing
    // insert 0 in the vector
    A.insert(A.begin(), 0);
  
    // Updated size of the vector
    int n = (int)A.size();
  
    // Updating the bit tree
    for (int i = 1; i < n; ++i) {
        update(i, A[i], bit);
    }
  
    // Vector to store the answers
    // of range sum
    vector<int> ans;
  
    // Iterating in the query
    // vector
    for (auto i : B) {
  
        int type = i[0];
        int x = i[1], v = i[2];
  
        // If the query is of
        // type 1
        if (type == 1) {
  
            // Updating the tree
            // with x in the last
            update(n, x, bit);
  
            // Pushing back the value
            // in the original vector
            A.push_back(x);
  
            // Incrementing the size
            // of the vector by 1
            n++;
        }
  
        // If the query is of type 2
        else if (type == 2) {
  
            // Getting the updated index
            // in case of any query of
            // type 3 occured before it
            int id = x + sum(x, idx);
  
            // Making the effect to that
            // value to 0 by subtracting
            // same vaule from the tree
            update(id, -A[id], bit);
  
            // Updating the A[id] to v
            A[id] = v;
  
            // Now update the
            // bit by v at x
            update(id, v, bit);
        }
  
        // If the query is of type 3
        else if (type == 3) {
  
            // Get the current index
            int id = x + sum(x, idx);
  
            // Remove the effect of that
            // index from the bit tree
            update(id, -A[id], bit);
  
            // Update the idx tree
            // because one element has
            // been deleted
            update(x, 1, idx);
  
            // Update the idx val to 0
            A[id] = 0;
        }
  
        // If the query is of type 4
        else {
  
            // Get the updated range
            int xx = x + sum(x, idx);
            int vv = v + sum(v, idx);
  
            // Push_back the value
            ans.push_back(sum(vv, bit)
                          - sum(xx - 1, bit));
        }
    }
  
    return ans;
}
  
// Driver code
int main()
{
    vector<int> A = { 1, 2, 5, 3, 4 };
  
    // Queries
    vector<vector<int> > B = {
        { 4, 2, 4 },
        { 3, 3, 0 },
        { 1, 6, 0 },
        { 4, 3, 5 },
    };
  
    // Get the answer array
    vector<int> ans = peformQueries(A, B);
  
    // printing the answer
    for (int i : ans)
        cout << i << "\n";
  
    return 0;
}

chevron_right


Java

filter_none

edit
close

play_arrow

link
brightness_4
code

// Java implementation of the approach 
import java.util.*;
  
class GFG {
  
    // Size of the array (MAX)
    static int N = (int) 2e5 + 10;
  
    // To store the sum of
    // the array elements
    static int[] bit = new int[N];
  
    // To keep track of how many type 3
    // queries have been performed before
    // a particular index
    static int[] idx = new int[N];
  
    // Function to perform the point
    // update in Fenwick tree
    static void update(int idx, int val, int[] bitt) {
        while (idx < N) {
            bitt[idx] += val;
            idx += idx & (-idx);
        }
    }
  
    // Function to return the sum
    // of the elements A[1...idx]
    // from BIT
    static int sum(int idx, int[] bitt) {
  
        int res = 0;
        while (idx > 0) {
            res += bitt[idx];
            idx -= idx & (-idx);
        }
  
        return res;
    }
  
    // Function to perform the queries and
    // return the answer vector
    static Vector<Integer> peformQueries(Vector<Integer> A, int[][] B) {
  
        // For 1 bases indexing
        // insert 0 in the vector
        A.insertElementAt(0, 0);
  
        // Updated size of the vector
        int n = (int) A.size();
  
        // Updating the bit tree
        for (int i = 1; i < n; ++i) {
            update(i, A.elementAt(i), bit);
        }
  
        // Vector to store the answers
        // of range sum
        Vector<Integer> ans = new Vector<>();
  
        // Iterating in the query
        // vector
        for (int[] i : B) {
  
            int type = i[0];
            int x = i[1], v = i[2];
  
            // If the query is of
            // type 1
            if (type == 1) {
  
                // Updating the tree
                // with x in the last
                update(n, x, bit);
  
                // Pushing back the value
                // in the original vector
                A.add(x);
  
                // Incrementing the size
                // of the vector by 1
                n++;
            }
  
            // If the query is of type 2
            else if (type == 2) {
  
                // Getting the updated index
                // in case of any query of
                // type 3 occured before it
                int id = x + sum(x, idx);
  
                // Making the effect to that
                // value to 0 by subtracting
                // same vaule from the tree
                update(id, -A.elementAt(id), bit);
  
                // Updating the A[id] to v
                A.set(id, v);
  
                // Now update the
                // bit by v at x
                update(id, v, bit);
            }
  
            // If the query is of type 3
            else if (type == 3) {
  
                // Get the current index
                int id = x + sum(x, idx);
  
                // Remove the effect of that
                // index from the bit tree
                update(id, -A.elementAt(id), bit);
  
                // Update the idx tree
                // because one element has
                // been deleted
                update(x, 1, idx);
  
                // Update the idx val to 0
                A.set(id, 0);
            }
  
            // If the query is of type 4
            else {
  
                // Get the updated range
                int xx = x + sum(x, idx);
                int vv = v + sum(v, idx);
  
                // Push_back the value
                ans.add(sum(vv, bit) - sum(xx - 1, bit));
            }
        }
  
        return ans;
    }
  
    // Driver Code
    public static void main(String[] args) {
        Integer[] a = { 1, 2, 5, 3, 4 };
        Vector<Integer> A = new Vector<Integer>(Arrays.asList(a));
  
        // Queries
        int[][] B = { { 4, 2, 4 }, { 3, 3, 0 }, { 1, 6, 0 }, { 4, 3, 5 } };
  
        // Get the answer array
        Vector<Integer> ans = peformQueries(A, B);
  
        // printing the answer
        for (int i : ans)
            System.out.println(i);
    }
}
  
// This code is contributed by
// sanjeev2552

chevron_right


Python3

filter_none

edit
close

play_arrow

link
brightness_4
code

# Python implementation of the approach
  
# Size of the array (MAX)
N = int(2e5) + 10
  
# To store the sum of
# the array elements
bit = [0] * N
  
# To keep track of how many type 3
# queries have been performed before
# a particular index
idx = [0] * N
  
# Function to perform the point
# update in Fenwick tree
def update(index: int, val: int, bitt: list):
    while index < N:
        bitt[index] += val
        index += index & -index
  
# Function to return the sum
# of the elements A[1...idx]
# from BIT
def summation(index: int, bitt: list) -> int:
    res = 0
    while index > 0:
        res += bitt[index]
        index -= index & -index
    return res
  
# Function to perform the queries and
# return the answer vector
def performQueries(A: list, B: list) -> list:
    global bit, idx
  
    # For 1 bases indexing
    # insert 0 in the vector
    A.insert(0, 0)
  
    # Updated size of the vector
    n = len(A)
  
    # Updating the bit tree
    for i in range(1, n):
        update(i, A[i], bit)
  
    # Vector to store the answers
    # of range sum
    ans = []
  
    # Iterating in the query
    # vector
    for i in B:
        type = i[0]
        x = i[1]
        v = i[2]
  
        # If the query is of
        # type 1
        if type == 1:
  
            # Updating the tree
            # with x in the last
            update(n, x, bit)
  
            # Pushing back the value
            # in the original vector
            A.append(x)
  
            # Incrementing the size
            # of the vector by 1
            n += 1
  
        # If the query is of type 2
        elif type == 2:
  
            # Getting the updated index
            # in case of any query of
            # type 3 occured before it
            id = x + summation(x, idx)
  
            # Making the effect to that
            # value to 0 by subtracting
            # same vaule from the tree
            update(id, -A[id], bit)
  
            # Updating the A[id] to v
            A[id] = v
  
            # Now update the
            # bit by v at x
            update(id, v, bit)
  
        # If the query is of type 3
        elif type == 3:
  
            # Get the current index
            id = x + summation(x, idx)
  
            # Remove the effect of that
            # index from the bit tree
            update(id, -A[id], bit)
  
            # Update the idx tree
            # because one element has
            # been deleted
            update(x, 1, idx)
  
            # Update the idx val to 0
            A[id] = 0
  
        # If the query is of type 4
        else:
  
            # Get the updated range
            xx = x + summation(x, idx)
            vv = v + summation(v, idx)
  
            # Push_back the value
            ans.append(summation(vv, bit) - summation(xx - 1, bit))
    return ans
  
  
# Driver Code
if __name__ == "__main__":
    A = [1, 2, 5, 3, 4]
  
    # Queries
    B = [[4, 2, 4], [3, 3, 0], [1, 6, 0], [4, 3, 5]]
  
    # Get the answer array
    ans = performQueries(A, B)
  
    # printing the answer
    for i in ans:
        print(i)
  
# This code is contributed by
# sanjeev2552

chevron_right


Output:

10
13

Time Complexity: O(Q * logN + NlogN)

competitive-programming-img




My Personal Notes arrow_drop_up

Check out this Author's contributed articles.

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.



Improved By : sanjeev2552