3080. Mark Elements on Array by Performing Queries
Problem Description
You are provided with a 0-indexed array nums
of size n
, containing positive integers. Additionally, there's a 2D array queries
of size m
, where each query is represented as queries[i] = [index_i, k_i]
.
Initially, all elements of the nums
array are considered unmarked.
For each query, execute the following steps:
- Mark the element at
index_i
if it is not marked already. - Subsequently, mark
k_i
unmarked elements in the array having the smallest values. If there are ties in values, prefer the elements with smaller indices. In cases where fewer thank_i
unmarked elements remain, mark all that are available.
The goal is to return an array answer
of size m
, where answer[i]
is the sum of unmarked elements in the array after performing the i
-th query.
Intuition
To solve this problem, the idea is to efficiently manage and track the marking process while updating the sum of unmarked elements.
-
Pre-computation of Sum: Begin by calculating the total sum
s
of thenums
array. This allows for efficient updates whenever elements are marked and removed from the sum. -
Tracking Marked Status: Use a list
mark
to track which elements innums
have been marked. This helps in determining which elements contribute to the remaining sum after each query. -
Sorting for Efficiency: Convert
nums
into an array of tuplesarr
, where each tuple contains the value and its corresponding index. Sortingarr
by value (and by index if values are equal) facilitates the process of identifying thek_i
smallest unmarked elements efficiently with each query. -
Simulating the Queries: For each query
[index, k]
:- First, mark the element at the provided index if it isn't already marked, and subtract its value from the total sum.
- Next, iterate through the sorted array
arr
to mark thek_i
smallest unmarked elements, updating the sum and managing the marked status as elements are processed.
By following this efficient method, each query's impact is observed through small, incremental adjustments of the sum, and an answer is prepared in a straightforward manner.
Learn more about Sorting and Heap (Priority Queue) patterns.
Solution Approach
The solution employs a systematic approach to handle the marking of elements and tracking the sum of unmarked elements efficiently. Let's break down the implementation:
-
Calculate Initial Sum: Start by computing the total sum
s
of the arraynums
. This sum will be adjusted as elements are marked through the queries. -
Mark Tracking: Initialize a list
mark
of the same length asnums
, with all values set toFalse
. This list helps in identifying which elements are marked as queries are processed. -
Preparation of a Sorted Array: Construct an array
arr
with tuples(x, i)
for each element innums
, wherex
is the value andi
is the index. Sortarr
by the values first, and by the indices second if the values are the same. This sorted array allows quick retrieval of the smallest unmarked values. -
Processing Each Query:
- For each query
[index, k]
, first check if the element atindex
is unmarked:- If unmarked, mark it and deduct its value from
s
.
- If unmarked, mark it and deduct its value from
- Next, use the sorted array
arr
to find and mark thek
smallest unmarked elements:- Iterate over
arr
, marking elements, deducting their values froms
, and adjustingk
until eitherk
elements are marked or no unmarked elements remain.
- Iterate over
- After processing each query, append the current value of
s
to the result listans
.
- For each query
-
Return Result: After all queries have been processed, return the list
ans
which contains the sum of unmarked elements after each query.
The above approach efficiently manages the task by leveraging sorting and an auxiliary data structure to keep track of marked status, providing an optimized solution to the problem.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's consider a small example to illustrate the solution approach:
Suppose we have the array nums = [5, 3, 8, 1, 7]
and the queries given by queries = [[1, 2], [3, 1]]
.
To solve using the described approach:
-
Calculate Initial Sum: The total sum of
nums
iss = 5 + 3 + 8 + 1 + 7 = 24
. -
Mark Tracking: Initialize a
mark
list as[False, False, False, False, False]
, indicating no elements are marked yet. -
Preparation of a Sorted Array: Prepare
arr
as[(1, 3), (3, 1), (5, 0), (7, 4), (8, 2)]
. This is sorted by value and index. -
Processing Each Query:
-
First Query
[1, 2]
:- The element at index
1
innums
is3
, which is unmarked. Mark it and updates
to24 - 3 = 21
. - From
arr
, mark the2
smallest unmarked elements:- Start with
(1, 3)
, marking index3
, updates = 21 - 1 = 20
. - Next, mark
(5, 0)
, updates = 20 - 5 = 15
.
- Start with
- After processing this query, append
15
toans
.
- The element at index
-
Second Query
[3, 1]
:- The element at index
3
innums
(value1
) is already marked; no update required fors
. - Mark the
1
smallest unmarked element usingarr
:- Pick
(7, 4)
, marking index4
, updates = 15 - 7 = 8
.
- Pick
- After completing this query, append
8
toans
.
- The element at index
-
-
Return Result: After all queries are complete,
ans = [15, 8]
, which represents the sum of unmarked elements after each query.
Solution Implementation
1from typing import List
2
3class Solution:
4 def unmarkedSumArray(self, nums: List[int], queries: List[List[int]]) -> List[int]:
5 n = len(nums)
6 total_sum = sum(nums) # Compute the sum of all elements in nums.
7 marked = [False] * n # Initialize a list to keep track of marked elements.
8 # Create a sorted list of tuples containing elements from nums and their indices.
9 # This helps in efficiently accessing the smallest unmarked elements.
10 sorted_nums = sorted((value, idx) for idx, value in enumerate(nums))
11
12 pointer = 0 # Initialize a pointer for traversing sorted elements.
13 results = [] # List to store the result of each query.
14
15 # Iterate over each query consisting of index and k value.
16 for index, k in queries:
17 # If the element at 'index' isn't marked, mark it and subtract its value from total_sum.
18 if not marked[index]:
19 marked[index] = True
20 total_sum -= nums[index]
21
22 # Process as many smallest unmarked elements as indicated by 'k'.
23 # Pointer ensures no element is processed more than once unnecessarily.
24 while k > 0 and pointer < n:
25 # Check if the current element in sorted order is unmarked.
26 if not marked[sorted_nums[pointer][1]]:
27 # Mark it and subtract its value from total_sum.
28 marked[sorted_nums[pointer][1]] = True
29 total_sum -= sorted_nums[pointer][0]
30 k -= 1 # Decrement k as one unmarked element is processed.
31
32 pointer += 1 # Move to the next element in sorted order.
33
34 # Append the current unmarked sum to results.
35 results.append(total_sum)
36
37 return results
38
1import java.util.Arrays;
2
3class Solution {
4 public long[] unmarkedSumArray(int[] nums, int[][] queries) {
5 int n = nums.length; // Length of the nums array
6 long totalSum = Arrays.stream(nums).asLongStream().sum(); // Calculate total sum of nums array
7 boolean[] marked = new boolean[n]; // Array to track marked indices
8 int[][] indexedNums = new int[n][2]; // Array to hold nums with their indices
9
10 // Populate indexedNums with values and their original indices
11 for (int i = 0; i < n; ++i) {
12 indexedNums[i] = new int[] {nums[i], i};
13 }
14
15 // Sort indexedNums based on values, then indices
16 Arrays.sort(indexedNums, (a, b) -> a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]);
17
18 int m = queries.length; // Number of queries
19 long[] results = new long[m]; // Array to store results of each query
20
21 // Process each query
22 for (int i = 0, sortedIndex = 0; i < m; ++i) {
23 int index = queries[i][0]; // Index specified in the query
24 int k = queries[i][1]; // Number of elements to mark
25
26 // Mark the specified index if not already marked
27 if (!marked[index]) {
28 marked[index] = true;
29 totalSum -= nums[index]; // Subtract marked value from total sum
30 }
31
32 // Mark the next k unmarked smallest elements
33 for (; k > 0 && sortedIndex < n; ++sortedIndex) {
34 int currentIndex = indexedNums[sortedIndex][1];
35 if (!marked[currentIndex]) {
36 marked[currentIndex] = true;
37 totalSum -= indexedNums[sortedIndex][0]; // Subtract marked value from total sum
38 --k; // Decrease k as one element is marked
39 }
40 }
41
42 results[i] = totalSum; // Store current unmarked sum in results
43 }
44
45 return results; // Return the results for each query
46 }
47}
48
1#include <vector>
2#include <algorithm>
3#include <numeric>
4
5using namespace std;
6
7class Solution {
8public:
9 vector<long long> unmarkedSumArray(vector<int>& nums, vector<vector<int>>& queries) {
10 int n = nums.size();
11
12 // Calculate the total sum of numbers in the array `nums`
13 long long totalSum = accumulate(nums.begin(), nums.end(), 0LL);
14
15 // Marker vector to check if an element has been unmarked
16 vector<bool> marked(n, false);
17
18 // Array to hold each number and its index for sorting purposes
19 vector<pair<int, int>> indexedNums;
20 for (int i = 0; i < n; ++i) {
21 indexedNums.emplace_back(nums[i], i);
22 }
23
24 // Sort `indexedNums` based on the values (first element of pairs)
25 sort(indexedNums.begin(), indexedNums.end());
26
27 // Result vector to store the sum for each query
28 vector<long long> result;
29
30 int m = queries.size();
31 int j = 0;
32
33 // Process each query
34 for (int i = 0; i < m; ++i) {
35 int index = queries[i][0];
36 int k = queries[i][1];
37
38 // If the number at `index` has not been marked before, mark it and subtract from totalSum
39 if (!marked[index]) {
40 marked[index] = true;
41 totalSum -= nums[index];
42 }
43
44 // Mark `k` smallest unmarked numbers and subtract them from totalSum
45 while (k > 0 && j < n) {
46 if (!marked[indexedNums[j].second]) {
47 marked[indexedNums[j].second] = true;
48 totalSum -= indexedNums[j].first;
49 --k;
50 }
51 ++j;
52 }
53
54 // Collect the modified totalSum for the current query
55 result.push_back(totalSum);
56 }
57
58 return result;
59 }
60};
61
1function unmarkedSumArray(nums: number[], queries: number[][]): number[] {
2 const n = nums.length; // Length of the input numbers array
3 let totalSum = nums.reduce((acc, x) => acc + x, 0); // Calculate the total sum of the array
4 const mark: boolean[] = Array(n).fill(false); // Boolean array to keep track of marked indices
5 const indexedNums = nums.map((value, index) => [value, index]); // Pair each number with its index
6 indexedNums.sort((a, b) => (a[0] === b[0] ? a[1] - b[1] : a[0] - b[0])); // Sort by value, and by index if values are equal
7
8 let j = 0; // Initialize pointer for sorted array
9 const results: number[] = []; // Array to hold results of queries
10
11 for (let [index, k] of queries) {
12 // If the element at 'index' is not already marked, mark it and subtract its value
13 if (!mark[index]) {
14 mark[index] = true;
15 totalSum -= nums[index];
16 }
17
18 // Process 'k' elements from sorted indexed array
19 for (; k > 0 && j < n; ++j) {
20 if (!mark[indexedNums[j][1]]) {
21 mark[indexedNums[j][1]] = true;
22 totalSum -= indexedNums[j][0];
23 --k;
24 }
25 }
26
27 // Store the current unmarked sum
28 results.push(totalSum);
29 }
30
31 return results; // Return the results array with answers to all queries
32}
33
Time and Space Complexity
The time complexity of the code is O(n \times \log n)
. This is due to the sorting operation sorted((x, i) for i, x in enumerate(nums))
that sorts the nums
array based on values, which takes O(n \times \log n)
time. The rest of the operations in the function (the loops and checks) operate at most in linear time related to the length of nums
and queries
.
The space complexity of the code is O(n)
. This is because of the additional data structures used: mark
, arr
, and ans
. The list mark
is of length n
, arr
is also an array of n
elements, and ans
will store results for each query, leading to an overall space usage proportional to n
.
Learn more about how to find time and space complexity quickly.
A heap is a ...?
Recommended Readings
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
https algomonster s3 us east 2 amazonaws com cover_photos heap svg Priority Queue and Heap What is the relationship between priority queue and heap Priority Queue is an Abstract Data Type and Heap is the concrete data structure we use to implement a priority queue Priority Queue A priority queue
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!