Last Updated: 19 Mar, 2021

Boxes and chocolates

Ninja
Asked in companies
AppleDeloitte

Problem statement

Kevin has three children and he wants to buy chocolates for them. The shopkeeper has ‘N’ boxes of chocolates (numbered from 1 to ‘N’). Each box has some amount of chocolates. If Kevin selects the “i-th” box for buying the chocolates then he must have to buy all the chocolates present in that box. Kevin wants to distribute an equal amount of chocolates among his children.

This problem is divided into 3 subproblems. You must have to solve all three subproblems.

FIRST:

You have to find the maximum number of ways in which Kevin can buy the chocolates if he is allowed to choose boxes in random order.

SECOND:

You have to find the maximum number of ways in which Kevin can buy the chocolates if he is only allowed to choose the consecutive boxes.

THIRD:

There are ‘Q’ queries given to you and each query can be of two types:

0 I V
1 L R

The first type has 0 as a first character that represents this is an update query in which you have to update the number of chocolates in the “Ith” box to ‘V’.

The second type has 1 as a first character that represents this is a range query in which you have to find the maximum number of ways in which Kevin can buy the chocolates in the range [L, R] if he is allowed to choose boxes in any order.

Note:

1. There is no need to maximize the number of chocolates bought by Kevin. 
2. Consider 1 based indexing for queries. There must be a single query of type 1. 
3. You have to take the modulo with 10^9 + 7 as the result can be very large.
Input Format:
The first line contains a single integer ‘T’ representing the number of test cases. 

The first line of each test case will contain a single integer ‘N’ which denotes the number of chocolate boxes in the shop.

The second line of each test case contains ‘N’ space-separated integers which denote the number of chocolates in a particular box.

The third line of each test case will contain a single integer ‘Q’ which denotes the number of queries.

The next ‘Q’ lines contain three integers “0 I V” or “1 L R” in which ‘I’ represents the index whose value will have to be updated to ‘V’. ‘L’ and ‘R’ represent the inclusive range for which the result has been asked.
Output Format:
For each test case, print the result of all three subproblems in order such that:

In the first line, the result of subproblem 1 (FIRST) is printed.

In the second line, the result of subproblem 2 (SECOND) is printed.

In the third line, the result of each range query of type 1 is printed and each integer must be separated by a single space.

Output for every test case will be printed in a separate line.
Note:
You don’t need to print anything; It has already been taken care of. Just implement the given function.
Constraints:
1 <= T <= 10
1 <= N <= 100
1 <= ARR[ i ] <= 10^5
1 <= Q <= 100
1 <= I, L, and R <= N
1 <= V <= 10^5

Where "ARR[i]” is the number of chocolates in the “i-th” box, ‘Q’ is the number of queries, and ‘I’, ‘V’, ‘L’, and ‘R’ are described in the Input format.

Time limit: 1 sec

Approaches

01 Approach

The first thing that we have to consider is that Kevin will only buy chocolates in the multiple of 3 because he has three children and he wants to distribute chocolates in an equal amount.

 

For the FIRST subproblem: 

 

The basic idea is to go through recursively and keep counting the subsequences whose sum is divisible by 3.

 

We will make a helper function ( named “subsequenceOf3” ) which takes the given boxes, index of a particular box, and the current sum of chocolates as the arguments and returns the maximum number of ways in which Kevin can buy the chocolates if he chooses boxes in random order. Make sure of taking modulo with 10^9 + 7.

 

int subsequenceOf3 (vector<int> &arr, int ind, int sum)

 

Where ‘arr' is the given array/boxes, ‘ind’ is the number of boxes left, and ‘sum’ is the sum of chocolates.

 

The steps are as follows:

 

  1. In the “subsequenceOf3” function
    • if ‘IND’ becomes 0 then return 1 if ‘SUM’+ ‘ARR’[ i ] is divisible by 3, Otherwise return 0.
    • If, ‘SUM’ + ‘ARR[ IND ]’ is divisible by 3 then return 1 + two function calls with arguments as ('ARR', ‘IND’ - 1, ‘SUM’) and ('ARR', ‘IND’ - 1, ‘SUM’ + ‘ARR’[ i ]) by taking modulo with 10^9+7.
    • Else, return the summation of two function calls i.e. subsequenceOf3('ARR', ‘IND’ - 1, ‘SUM’) and subsequenceOf3('ARR', ‘IND’ - 1, ‘SUM’ + ‘ARR[ IND ]') by taking modulo with 10^9+7.
  2. In the given function just call the “subsequenceOf3” function.

 

For the SECOND subproblem:

 

The basic idea is to traverse through all the subarrays using a nested loop and increment the counter if the sum of the elements of a particular subarray is a multiple of 3.

 

We are going to use an additional function (named “subarrayOf3”) which takes boxes as the input and returns the maximum number of ways in which Kevin can buy the chocolates if he can only choose consecutive boxes.

 

int subarrayOf3(vector<int> &arr, int N)

 

Where ‘ARR’ is the given array/boxes and ‘N’ is the total number of boxes.

 

The steps are as follows:

 

  1. In the “subarrayOf3” function
    • Create a variable ‘COUNT’ to store the count of subarrays whose sum is the multiple of 3. Initialize ‘COUNT’ with 0.
    • Iterate through ‘ARR’ (say, iterator = ‘i’)
      • Create another variable ‘SUM’ to store the sum of elements and initialize it with 0.
      • Iterate through ‘ARR’ again but from ‘i’ to ‘N’ - 1. (say, iterator = ‘j’)
        • Add ‘ARR[ j ]’ to sum and check if the sum is now a multiple of 3 then increment the ‘COUNT’ by 1.
    • Return ‘COUNT’.
  2. In the given function call “subarrayOf3”.

 

For the THIRD subproblem:

 

The basic idea is to use a segment tree to find the maximum number of ways in which Kevin can buy the chocolates within a range ['L', ‘R’] (inclusive) if he chooses boxes in any order (means we have to find the number of subsequences having sum divisible by 3). We will store three integer variables at each node by using struct (in case of CPP):

 

struct node { 

int rem0 ( it stores the frequency of numbers having remainder 0 when divided by 3).

int rem1 ( it stores the frequency of numbers having remainder 1 when divided by 3).

int rem2 ( it stores the frequency of numbers having remainder 2 when divided by 3).

}

 

For Reference: https://cp-algorithms.com/data_structures/segment_tree.html

 

And, the merge function (receives two nodes n1 and n2) is used to find the merged node which computes new rem0, rem1, and rem2 by taking modulo with 10^9+7 as:

 

new rem0 = ((n1.rem0 * n2.rem0)%mod + (n1.rem1 * n2.rem2)%mod + (n1.rem2 * n2.rem1)%mod + n1.rem0 + n2.rem0)%mod

new rem1 =  ((n1.rem0 * n2.rem1)%mod + (n1.rem1 * n2.rem0)%mod + n1.rem1 + n2.rem1 + (n1.rem2 * n2.rem2)%mod)%mod

new rem2  = ((n1.rem0 * n2.rem2)%mod + (n1.rem2 * n2.rem0)%mod + n1.rem2 + n2.rem2 + (n1.rem1 * n2.rem1)%mod)%mod

 

Where ‘mod’ is equal to 10^9+7.

 

The steps are as  follows:

 

  1. Make a merge function as described above that must return a merged node.
  2. Make a build function that will build the segment tree from 0 to ‘N’ - 1 and at each node, it must store ‘REM0’, ‘REM1’, and ‘REM2’.
  3. Make an update function that will be used to update the segments of the segment tree that affect due to the change in the value of ‘ARR[ i ]’.
  4. Make a query function and code as
    • Check if the segment range is out of the query range then create a struct of type node (which contains  ‘REM0’, ‘REM1’, and ‘REM2’) and return it.
    • Check if the segment range is completely inside the query range then directly return the current node of the segment tree.
    • Compute mid-value of segment range.
    • Call the query function for the left of the segment tree and right of the segment tree and store the obtained result in two variables.
    • Merge the above-obtained result and then return it.
  5. In the given function
    • Create a vector named ‘ST’ of type ‘NODE’ that represents the segment tree.
    • Call the build function.
    • Call the update and query function for the respective type of queries.
    • Store the result of each range query in a vector.
    • Return the result of all the subproblems after storing them in a vector of vectors.

02 Approach

For the FIRST subproblem: 

 

The basic idea is to reduce the time complexity by using Dynamic Programming which will cut down the repeated recursive calls that were taking too much time in the recursive approach (discussed in approach 1).

 

We will create a ‘DP’ table of size ‘N’*3, where ‘DP’[ i ][ j ] = the number of subsequences of ‘ARR’[ 0...i ] with remainder equal to j and we will compute the value of ‘DP’ for all 0 <= ‘i’ < ‘N’ and 0 <= ‘j’ < 3. A row represents the box counts and a column represents the remainder when the sum of a subsequence is divided by 3.

 

We will make a helper function ( named “subsequenceOf3” ) to solve this subproblem.

 

int subsequenceOf3 (vector<int> &arr)

 

Where ‘arr' is the given array/boxes.

 

The steps are as follows:

 

  1. In the “subsequenceOf3” function
    • Set 0 for every ‘DP’[ i ][ j ].
    • Make ‘DP’[ 0 ][ ‘ARR’[ 0 ] % 3] = 1 (for the first element of the array), this done because the rest ‘DP’ will be filled by using the values stored in the previous row.
    • Iterate through the ‘ARR’ from 1 to ‘N’ - 1. (say, iterator = ‘i’)
      • Make a temporary array ‘TEMP’ of size 3 store the new counts.
      • For each ‘j’, add the value of ‘ARR’[ i ] with the ‘j’, take modulo 3 and consider it as the new value of ‘j’ and add the result obtained with ‘i’ - 1 in the ‘TEMP’. Make sure of taking modulo with 10^9 + 7.
      • Now, for the box count ‘i’ store the result obtained from the box count ‘i’ - 1 in ‘DP’[ i ][ j ] (for every ‘j’) and add the corresponding value stored in ‘TEMP’. Make sure of taking modulo with 10^9 + 7.
    • Return ‘DP’[ ‘N’ - 1 ][ 0 ].
  2. In the given function just call the “subsequenceOf3” function.

 

For the SECOND subproblem:

 

The basic idea is to traverse through the array and keep storing the frequency of subarrays as value and the remainder that we get after dividing the sum of this subarray by 3 as key in a HashMap. Add the frequency of subarrays to the counter that gives the pre-stored remainder when divided by 3.

 

We are going to use an additional function (named “subarrayOf3”)

 

int subarrayOf3(vector<int> &arr)

 

Where ‘arr’ is the given array/boxes.

 

The steps are as follows:

 

  1. In the “subarrayOf3” function
    • Create a variable ‘COUNT’ to store the count of subarrays whose sum is the multiple of 3. Initialize ‘COUNT’ with 0.
    • Create a HaspMap named ‘MP’.
    • Create another variable ‘SUM’ to store the sum of elements and initialize it with 0.
    • Iterate through ‘ARR’. (say, iterator = ‘i’)
      • Add ‘ARR’[ i ] to the ‘SUM’.
      • Check if ‘SUM’ is divisible by 3 then increment the ‘COUNT’ by 1.
      • Store the remainder obtained when we divide ‘SUM’ by 3 in a variable named ‘X’.
      • Check if ‘X’ is already in HashMap then increment ‘COUNT’ by the ‘MP[ X ]’.
      • Increment ‘MP[ X ]’ by 1.
    • Return ‘COUNT’.
  2. In the given function call “subarrayOf3”.

 

For the THIRD subproblem:

 

The approach for this subproblem is the same as mentioned in “approach 1”.