Open In App

Range and Update Sum Queries with Factorial

Improve
Improve
Like Article
Like
Save
Share
Report

Given an array arr[] of N integers and number of queries Q. The task is to answer three types of queries.

  1. Update [l, r] – for every i in range [l, r] increment arr[i] by 1.
  2. Update [l, val] – change the value of arr[l] to val.
  3. Query [l, r] – calculate the sum of arr[i]! % 109 for all i in range [l, r] where arr[i]! is the factorial of arr[i].

Prerequisite :Binary Indexed Trees | Segment Trees Examples:

Input: Q = 6, arr[] = { 1, 2, 1, 4, 5 } 3 1 5 1 1 3 2 2 4 3 2 4 1 2 5 3 1 5 Output: 148 50 968 1st query, the required sum is (1! + 2! + 1! + 4! + 5!) % 109 = 148 2nd query, the array becomes arr[] = { 2, 3, 2, 4, 5 } 3rd query, array becomes arr[] = { 2, 4, 2, 4, 5 } 4th query, the required sum is (4! + 2! + 4!) % 109 = 50 5th query, the array becomes arr[] = { 2, 5, 3, 5, 6 } 6th query, the required sum is (2! + 5! + 3! + 5! + 6!) % 109 = 968

Naive Approach: A simple solution is to run a loop from l to r and calculate sum of factorial of elements (pre-computed) in the given range for the 3rd query. For the 2nd query, to update a value, simply replace arr[i] with the given value i.e. arr[i] = val. For the 1st type query, increment the value of arr[i] i.e. arr[i] = arr[i] + 1. Efficient Approach: It can be observed from careful analysis that 40! is divisible by 109, that means factorial of every number greater than 40 will be divisible by 109. Hence, that adds zero to our answer for the 3rd query. The idea is to reduce the time complexity for each query and update operation to O(logN). Use Binary Indexed Trees (BIT) or Segment Trees. Construct a BIT[] array and have two functions for query and update operation.

  • Now, for each update operation of the 1st type, the key observation is that the number in given range can at max be updated to 40, since after that it won’t matter as it will add zero to our final answer. We will use a set to store the index of only those numbers which are lesser than 10 and use binary search to find the l index of the update query and increment the l index until every element is updated in range of that update query. If the arr[i] becomes greater than or equal to 40 after incrementing by 1, remove it from the set as it will not affect our answer of sum query even after any next update query.
  • For the update operation of the 2nd type, call the update function with the given value. Also, the given value is < 40, insert the index of the element to be replaced with into the set and if the given value is ? 40, remove it from the set since it will have no importance in sum query.
  • For the sum query of the 3rd type, simply do query(r) – query(l – 1).

Below is the implementation of the above approach: 

C++




// CPP program to calculate sum of
// factorials in an interval and update
// with two types of operations
#include <bits/stdc++.h>
using namespace std;
 
// Modulus
const int MOD = 1e9;
 
// Maximum size of input array
const int MAX = 100;
 
// Size for factorial array
const int SZ = 40;
 
int BIT[MAX + 1], fact[SZ + 1];
 
// structure for queries with members type,
// leftIndex, rightIndex of the query
struct queries {
    int type, l, r;
};
 
// function for updating the value
void update(int x, int val, int n)
{
    for (x; x <= n; x += x & -x)
        BIT[x] += val;
}
 
// function for calculating the required
// sum between two indexes
int sum(int x)
{
    int s = 0;
    for (x; x > 0; x -= x & -x)
        s += BIT[x];
    return s;
}
 
// function to return answer to queries
void answerQueries(int arr[], queries que[],
                   int n, int q)
{
    // Precomputing factorials
    fact[0] = 1;
    for (int i = 1; i < 41; i++)
        fact[i] = (fact[i - 1] * i) % MOD;
 
    // Declaring a Set
    set<int> s;
    for (int i = 1; i < n; i++) {
 
        // inserting indexes of those
        // numbers which are lesser
        // than 40
        if (arr[i] < 40) {
            s.insert(i);
            update(i, fact[arr[i]], n);
        }
        else
            update(i, 0, n);
    }
 
    for (int i = 0; i < q; i++) {
 
        // update query of the 1st type
        if (que[i].type == 1) {
            while (true) {
 
                // find the left index of query in
                // the set using binary search
                auto it = s.lower_bound(que[i].l);
 
                // if it crosses the right index of
                // query or end of set, then break
                if (it == s.end() || *it > que[i].r)
                    break;
 
                que[i].l = *it;
                int val = arr[*it] + 1;
 
                // update the value of arr[i] to
                // its new value
                update(*it, fact[val] - fact[arr[*it]], n);
 
                arr[*it]++;
 
                // if updated value becomes greater
                // than or equal to 40 remove it from
                // the set
                if (arr[*it] >= 40)
                    s.erase(*it);
 
                // increment the index
                que[i].l++;
            }
        }
 
        // update query of the 2nd type
        else if (que[i].type == 2) {
            int idx = que[i].l;
            int val = que[i].r;
 
            // update the value to its new value
            update(idx, fact[val] - fact[arr[idx]], n);
 
            arr[idx] = val;
 
            // If the value is less than 40, insert
            // it into set, otherwise remove it
            if (val < 40)
                s.insert(idx);
            else
                s.erase(idx);
        }
 
        // sum query of the 3rd type
        else
            cout << (sum(que[i].r) - sum(que[i].l - 1))
                 << endl;
    }
}
 
// Driver Code to test above functions
int main()
{
    int q = 6;
 
    // input array using 1-based indexing
    int arr[] = { 0, 1, 2, 1, 4, 5 };
    int n = sizeof(arr) / sizeof(arr[0]);
 
    // declaring array of structure of type queries
    queries que[q + 1];
 
    que[0].type = 3, que[0].l = 1, que[0].r = 5;
    que[1].type = 1, que[1].l = 1, que[1].r = 3;
    que[2].type = 2, que[2].l = 2, que[2].r = 4;
    que[3].type = 3, que[3].l = 2, que[3].r = 4;
    que[4].type = 1, que[4].l = 2, que[4].r = 5;
    que[5].type = 3, que[5].l = 1, que[5].r = 5;
 
    // answer the Queries
    answerQueries(arr, que, n, q);
    return 0;
}


Python3




# Python3 program to calculate sum of
# factorials in an interval and update
# with two types of operations
from bisect import bisect_left as lower_bound
 
# Modulus
MOD = 1e9
 
# Maximum size of input array
MAX = 100
 
# Size for factorial array
SZ = 40
 
BIT = [0] * (MAX + 1)
fact = [0] * (SZ + 1)
 
# structure for queries with members type,
# leftIndex, rightIndex of the query
class queries:
    def __init__(self, tpe, l, r):
        self.type = tpe
        self.l = l
        self.r = r
 
# function for updating the value
def update(x, val, n):
    global BIT
    while x <= n:
        BIT[x] += val
        x += x & -x
 
# function for calculating the required
# sum between two indexes
def summ(x):
    global BIT
    s = 0
    while x > 0:
        s += BIT[x]
        x -= x & -x
    return s
 
# function to return answer to queries
def answerQueries(arr: list, que: list,
                       n: int, q: int):
    global fact
 
    # Precomputing factorials
    fact[0] = 1
    for i in range(1, 41):
        fact[i] = int((fact[i - 1] * i) % MOD)
 
    # Declaring a Set
    s = set()
    for i in range(1, n):
 
        # inserting indexes of those
        # numbers which are lesser
        # than 40
        if arr[i] < 40:
            s.add(i)
            update(i, fact[arr[i]], n)
        else:
            update(i, 0, n)
 
    for i in range(q):
 
        # update query of the 1st type
        if que[i].type == 1:
            while True:
                s = list(s)
                s.sort()
 
                # find the left index of query in
                # the set using binary search
                it = lower_bound(s, que[i].l)
 
                # if it crosses the right index of
                # query or end of set, then break
                if it == len(s) or s[it] > que[i].r:
                    break
 
                que[i].l = s[it]
                val = arr[s[it]] + 1
 
                # update the value of arr[i] to
                # its new value
                update(s[it], fact[val] -
                              fact[arr[s[it]]], n)
 
                arr[s[it]] += 1
 
                # if updated value becomes greater
                # than or equal to 40 remove it from
                # the set
                if arr[s[it]] >= 40:
                    s.remove(it)
 
                # increment the index
                que[i].l += 1
 
        # update query of the 2nd type
        elif que[i].type == 2:
            s = set(s)
            idx = que[i].l
            val = que[i].r
 
            # update the value to its new value
            update(idx, fact[val] - fact[arr[idx]], n)
 
            arr[idx] = val
 
            # If the value is less than 40, insert
            # it into set, otherwise remove it
            if val < 40:
                s.add(idx)
            else:
                s.remove(idx)
 
        # sum query of the 3rd type
        else:
            print((summ(que[i].r) -
                   summ(que[i].l - 1)))
 
# Driver Code
if __name__ == "__main__":
 
    q = 6
 
    # input array using 1-based indexing
    arr = [0, 1, 2, 1, 4, 5]
    n = len(arr)
 
    # declaring array of structure of type queries
    que = [ queries(3, 1, 5),
            queries(1, 1, 3),
            queries(2, 2, 4),
            queries(3, 2, 4),
            queries(1, 2, 5),
            queries(3, 1, 5) ]
 
    # answer the Queries
    answerQueries(arr, que, n, q)
 
# This code is contributed by
# sanjeev2552


C#




// C# program to calculate sum of
// factorials in an interval and update
// with two types of operations
using System;
using System.Linq;
using System.Collections.Generic;
 
class Program
{
  // Modulus
  private const int MOD = 1000000000;
 
  // Maximum size of input array
  private const int MAX = 100;
 
  // Size for factorial array
  private const int SZ = 40;
  private static int[] BIT = new int[MAX + 1];
  private static int[] fact = new int[SZ + 1];
 
  // structure for queries with members type,
  // leftIndex, rightIndex of the query
  private struct Queries
  {
    public int type;
    public int l;
    public int r;
 
    // Constructor
    public Queries(int x, int y, int z)
    {
      type = x;
      l = y;
      r = z;
    }
  };
 
  // function for updating the value
  private static void Update(int x, int val, int n)
  {
    for (; x <= n; x += x & -x)
    {
      BIT[x] += val;
    }
  }
 
  // function for calculating the required
  // sum between two indexes
  private static int Sum(int x)
  {
    int s = 0;
    for (; x > 0; x -= x & -x)
    {
      s += BIT[x];
    }
    return s;
  }
 
  // function to return answer to queries
  private static void AnswerQueries(int[] arr, Queries[] que, int n, int q)
  {
    // Precomputing factorials
    fact[0] = 1;
    for (int i = 1; i < 41; i++)
    {
      fact[i] = (fact[i - 1] * i) % MOD;
    }
 
    // Declaring a Set
    SortedSet<int> s = new SortedSet<int>();
    for (int i = 1; i < n; i++)
    {
      // inserting indexes of those
      // numbers which are lesser
      // than 40
      if (arr[i] < 40)
      {
        s.Add(i);
        Update(i, fact[arr[i]], n);
      }
      else
      {
        Update(i, 0, n);
      }
    }
 
    for (int i = 0; i < q; i++)
    {
      // update query of the 1st type
      if (que[i].type == 1)
      {
        int l = que[i].l;
        int r = que[i].r;
 
        // find the left index of query in
        // the set using binary search
        var it = s.GetViewBetween(l, r).GetEnumerator();
 
        // if it crosses the right index of
        // query or end of set, then break
        while (it.MoveNext())
        {
          int idx = it.Current;
          int val = arr[idx] + 1;
 
          // update the value of arr[i] to
          // its new value
          Update(idx, fact[val] - fact[arr[idx]], n);
          arr[idx] = val;
 
          // if updated value becomes greater
          // than or equal to 40 remove it from
          // the set
          if (arr[idx] >= 40)
          {
            s.Remove(idx);
          }
        }
      }
      // update query of the 2nd type
      else if (que[i].type == 2)
      {
        int idx = que[i].l;
        int val = que[i].r;
 
        // update the value to its new value
        Update(idx, fact[val] - fact[arr[idx]], n);
        arr[idx] = val;
 
        // If the value is less than 40, insert
        // it into set, otherwise remove it
        if (val < 40)
        {
          s.Add(idx);
        }
        else
        {
          s.Remove(idx);
        }
      }
 
      // sum query of the 3rd type
      else
      {
        Console.WriteLine(Sum(que[i].r) - Sum(que[i].l - 1));
      }
    }
  }
 
  // Driver Code to test above functions
  public static void Main(string[] args)
  {
    int q = 6;
 
    // input array using 1-based indexing
    int[] arr = { 0, 1, 2, 1, 4, 5 };
    int n = arr.Length;
 
    // declaring array of structure of type queries
    Queries[] que = new Queries[q + 1];
 
    que[0] = new Queries(3, 1, 5);
    que[1] = new Queries(1, 1, 3);
    que[2] = new Queries(2, 2, 4);
    que[3] = new Queries(3, 2, 4);
    que[4] = new Queries(1, 2, 5);
    que[5] = new Queries(3, 1, 5);
 
    // answer the Queries
    AnswerQueries(arr, que, n, q);
  }
}
 
// This code is contributed by phasing17.


Javascript




// JavaScript program to calculate sum of
// factorials in an interval and update
// with two types of operations
 
// Modulus
let MOD = 10000000000;
 
// Maximum size of input array
let MAX = 100;
 
// Size for factorial array
let SZ = 40;
 
let BIT = new Array(MAX + 1).fill(0);
 
let fact = new Array(SZ + 1).fill(0);
 
function lower_bound(arr, ele)
{
    for (var i = 0; i < arr.length; i++)
    {
        if (arr[i] >= ele)
            return i;
    }
    return arr.length;
}
 
// structure for queries with members type,
// leftIndex, rightIndex of the query
class queries
{
    constructor(tpe, l, r)
    {
        this.type = tpe;
        this.l = l;
        this.r = r;
    }
}
 
// function for updating the value
function update(BIT, x, val, n)
{
    while (x <= n)
    {
        BIT[x] += val;
        x += (x & -x);
    }
    return BIT
}
 
// function for calculating the required
// sum between two indexes
function summ(x)
{
    var s = 0;
    while (x > 0)
    {
        s += BIT[x];
        x -= x & -x;
    }
    return s;
}
 
// function to return answer to queries
function answerQueries(arr, que, n, q)
{
    // Precomputing factorials
    fact[0] = 1;
    for (var i = 1; i <= 40; i++)
        fact[i] = Number((fact[i - 1] * i) % MOD);
 
    // Declaring a Set
    var s = new Set();
    for (var i = 1; i < n; i++)
    {
        // inserting indexes of those
        // numbers which are lesser
        // than 40
        if (arr[i] < 40)
        {
            s.add(i);
            BIT = update(BIT, i, fact[arr[i]], n);
        }
        else
            BIT = update(BIT, i, 0, n);
    }
 
    for (var i = 0; i < q; i++)
    {
        // update query of the 1st type
        if (que[i].type == 1)
        {
            while (true)
            {
                s = Array.from(s);
                s.sort();
 
                // find the left index of query in
                // the set using binary search
                it = lower_bound(s, que[i].l);
 
                // if it crosses the right index of
                // query or end of set, then break
                if (it == s.length || s[it] > que[i].r)
                    break;
 
                que[i].l = s[it];
                val = arr[s[it]] + 1;
 
                // update the value of arr[i] to
                // its new value
                BIT = update(BIT, s[it], fact[val] -
                              fact[arr[s[it]]], n);
 
                arr[s[it]] += 1;
 
                // if updated value becomes greater
                // than or equal to 40 remove it from
                // the set
                if (arr[s[it]] >= 40)
                    s.splice(it, 1);
 
                // increment the index
                que[i].l += 1;
            }
        }
 
        // update query of the 2nd type
        else if (que[i].type == 2)
        {
            s = new Set(s);
            var idx = que[i].l;
            var val = que[i].r;
         
 
            //update the value to its new value
            BIT = update(BIT, idx, fact[val] - fact[arr[idx]], n);
 
            arr[idx] = val;
 
            // If the value is less than 40, insert
            // it into set, otherwise remove it
            if (val < 40)
                s.add(idx);
            else
                s.remove(idx);
        }
 
        // sum query of the 3rd type
        else
            console.log((summ(que[i].r) - summ(que[i].l - 1)));
    }
}
 
// Driver Code
 
let q = 6;
 
// input array using 1-based indexing
let arr = [0, 1, 2, 1, 4, 5];
let n = arr.length;
 
// declaring array of structure of type queries
let que = [ new queries(3, 1, 5), new queries(1, 1, 3),
        new queries(2, 2, 4),
        new queries(3, 2, 4),
        new queries(1, 2, 5),
        new queries(3, 1, 5) ];
 
// answer the Queries
answerQueries(arr, que, n, q);
 
// This code is contributed by phasing17


Java




// Java program to calculate sum of
// factorials in an interval and update
// with two types of operations
 
import java.util.SortedSet;
import java.util.TreeSet;
 
class GFG {
 
    // Modulus
    private static final int MOD = 1000000000;
 
    // Maximum size of input array
    private static final int MAX = 100;
 
    // Size for factorial array
    private static final int SZ = 40;
    private static int[] BIT = new int[MAX + 1];
    private static int[] fact = new int[SZ + 1];
 
    // structure for queries with members type,
    // leftIndex, rightIndex of the query
    private static class Queries {
        public int type;
        public int l;
        public int r;
 
        // Constructor
        public Queries(int type, int l, int r)
        {
            this.type = type;
            this.l = l;
            this.r = r;
        }
    }
 
    // function for updating the value
    private static void update(int x, int val, int n)
    {
        for (; x <= n; x += x & -x) {
            BIT[x] += val;
        }
    }
 
    // function for calculating the required
    // sum between two indexes
    private static int sum(int x)
    {
        int s = 0;
        for (; x > 0; x -= x & -x) {
            s += BIT[x];
        }
        return s;
    }
 
    // function to return answer to queries
    private static void
    answerQueries(int[] arr, Queries[] que, int n, int q)
    {
 
        // Precomputing factorials
        fact[0] = 1;
        for (int i = 1; i < 41; i++) {
            fact[i] = (fact[i - 1] * i) % MOD;
        }
 
        // Declaring a Set
        SortedSet<Integer> s = new TreeSet<>();
        for (int i = 1; i < n; i++) {
 
            // inserting indexes of those
            // numbers which are lesser
            // than 40
            if (arr[i] < 40) {
                s.add(i);
                update(i, fact[arr[i]], n);
            }
            else {
                update(i, 0, n);
            }
        }
 
        for (int i = 0; i < q; i++) {
            if (que[i].type == 1) {
 
                // find the left index of query in
                // the set using binary search
                SortedSet<Integer> view
                    = s.subSet(que[i].l, que[i].r + 1);
 
                // if it crosses the right index of
                // query or end of set, then break
                for (int idx : view) {
                    int val = arr[idx] + 1;
 
                    // update the value of arr[i] to
                    // its new value
                    update(idx, fact[val] - fact[arr[idx]],
                           n);
                    arr[idx] = val;
 
                    // if updated value becomes greater
                    // than or equal to 40 remove it from
                    // the set
                    if (arr[idx] >= 40) {
                        s.remove(idx);
                    }
                }
            }
            // update query of the 2nd type
            else if (que[i].type == 2) {
                int idx = que[i].l;
                int val = que[i].r;
                update(idx, fact[val] - fact[arr[idx]], n);
                arr[idx] = val;
 
                // If the value is less than 40, insert
                // it into set, otherwise remove it
                if (val < 40) {
                    s.add(idx);
                }
                else {
                    s.remove(idx);
                }
            }
 
            // sum query of the 3rd type
            else {
                System.out.println(sum(que[i].r)
                                   - sum(que[i].l - 1));
            }
        }
    }
 
    // Driver Code to test above functions
    public static void main(String[] args)
    {
        int q = 6;
        int[] arr = { 0, 1, 2, 1, 4, 5 };
        int n = arr.length;
 
        // declaring array of structure of type queries
        Queries[] que = new Queries[q + 1];
 
        que[0] = new Queries(3, 1, 5);
        que[1] = new Queries(1, 1, 3);
        que[2] = new Queries(2, 2, 4);
        que[3] = new Queries(3, 2, 4);
        que[4] = new Queries(1, 2, 5);
        que[5] = new Queries(3, 1, 5);
 
        // answer the Queries
        answerQueries(arr, que, n, q);
    }
}
 
// This code is contributed by phasing17.


Output:

148
50
968

Time Complexity: O(n+q) Since the set contains less than 40(constant) numbers, so its time complexity is treated as constant with respect to the large values of n and q.
Auxiliary Space: O(MAX+q)



Last Updated : 31 Jan, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads