Skip to content

Commit cad0ca7

Browse files
authored
feat: add sorting + stack + binary search method for lc No.2736 (#5234)
1 parent 7f35c53 commit cad0ca7

4 files changed

Lines changed: 371 additions & 0 deletions

File tree

solution/2700-2799/2736.Maximum Sum Queries/README.md

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,4 +406,138 @@ function maximumSumQueries(nums1: number[], nums2: number[], queries: number[][]
406406

407407
<!-- solution:end -->
408408

409+
<!-- solution:start -->
410+
411+
### 方法二:排序 + 单调栈 + 二分查找
412+
413+
首先,将所有查询按 $x$ 阈值降序排好,把全部数对按 $\textit{nums1}[i]$ 降序处理。
414+
迭代处理到第 $j$ 个查询时,将任何满足 $\textit{nums1}[i] \geq x_j$ 的数对加入单调栈。
415+
416+
单调栈的数对排序规则是:按 $\textit{nums2}[i]$ 升序、$\textit{nums1}[i] + \textit{nums2}[i]$ 降序。
417+
418+
如此保证栈中每个数对的 $\textit{nums2}[i]$ 更大时, $\textit{nums1}[i] + \textit{nums2}[i]$ 却更小,隔绝无效候选数对。
419+
420+
对于每个查询 $query_j$,靠二分查找在栈中找到第一个 $\textit{nums2}[i] \geq y_j$ 的数对,其对应的 $\textit{nums1}[i] + \textit{nums2}[i]$ 即为答案。
421+
422+
#### 复杂度解析
423+
424+
$n$ 是数组 $nums1$ 的长度,$m$ 是数组 $queries$ 的长度。
425+
- 时间复杂度:$O((n + m) \times \log n + m \times \log m)$。
426+
- 空间复杂度:$O(n + m)$。
427+
428+
<!-- tabs:start -->
429+
430+
#### Python3
431+
432+
```python
433+
class Solution:
434+
def maximumSumQueries(
435+
self, nums1: list[int], nums2: list[int], queries: list[list[int]]
436+
) -> list[int]:
437+
max_values = [-1] * len(queries)
438+
439+
queries = [(query[0], query[1], idx) for idx, query in enumerate(queries)]
440+
# Process queries by descending x threshold and y threshold.
441+
queries.sort(key=lambda x: (-x[0], -x[1]))
442+
443+
tuples: list[tuple[int, int]] = [] # Format: (num 1, num 2).
444+
for num_1, num_2 in zip(nums1, nums2):
445+
tuples.append((num_1, num_2))
446+
447+
# Process queries by descending num 1 and num 2.
448+
# Sort by ascending num 1 and num 2 to pop from the back.
449+
tuples.sort(key=lambda x: (x[0], x[1]))
450+
451+
stack: list[tuple[int, int]] = [] # Format: (num 2, sum).
452+
453+
for query_1, query_2, query_idx in queries:
454+
while tuples and tuples[-1][0] >= query_1: # Tuple's num 1 >= x threshold.
455+
num_1, num_2 = tuples.pop(-1)
456+
nums_sum = num_1 + num_2
457+
458+
while stack and stack[-1][0] < num_2 and stack[-1][1] <= nums_sum:
459+
stack.pop(-1) # Stack top isn't better than popped tuple.
460+
461+
insertion_idx = bisect_left(stack, (num_2, nums_sum))
462+
463+
if insertion_idx == len(stack):
464+
stack.insert(insertion_idx, (num_2, nums_sum))
465+
466+
elif stack[insertion_idx][1] < nums_sum:
467+
stack.insert(insertion_idx, (num_2, nums_sum))
468+
469+
search_idx = bisect_left(stack, (query_2, 0))
470+
if search_idx < len(stack):
471+
max_values[query_idx] = stack[search_idx][1]
472+
473+
return max_values
474+
```
475+
476+
#### C++
477+
478+
```cpp
479+
class Solution {
480+
public:
481+
vector<int> maximumSumQueries(vector<int>& nums1, vector<int>& nums2, vector<vector<int>>& queries) {
482+
vector<int> maxValues(queries.size(), -1);
483+
484+
vector<vector<int>> queriesIndices;
485+
for (int idx = 0; idx < queries.size(); idx++)
486+
queriesIndices.push_back({queries[idx][0], queries[idx][1], idx});
487+
488+
// Process queries by descending x threshold and y threshold.
489+
// Sort ascendingly and later pop from the back.
490+
sort(queriesIndices.begin(), queriesIndices.end());
491+
492+
vector<pair<int, int>> numsPairs; // Format: {num 1, num 2}.
493+
for (int idx = 0; idx < nums2.size(); idx++)
494+
numsPairs.push_back({nums1[idx], nums2[idx]});
495+
496+
// Process queries by descending num 1 and num 2.
497+
// Sort by ascending num 1 and num 2 to pop from the back.
498+
sort(numsPairs.begin(), numsPairs.end());
499+
500+
deque<pair<int, int>> stack; // Format: {num 2, sum}.
501+
502+
while (!queriesIndices.empty()) {
503+
int queryOne = queriesIndices.back()[0];
504+
int queryTwo = queriesIndices.back()[1];
505+
int queryIdx = queriesIndices.back()[2];
506+
queriesIndices.pop_back();
507+
508+
// Pair's num 1 >= x threshold.
509+
while (!numsPairs.empty() && numsPairs.back().first >= queryOne) {
510+
auto [numOne, numTwo] = numsPairs.back();
511+
numsPairs.pop_back();
512+
int numsSum = numOne + numTwo;
513+
514+
while (!stack.empty() and stack.back().first < numTwo and stack.back().second <= numsSum)
515+
stack.pop_back(); // Stack top isn't better than popped pair.
516+
517+
pair<int, int> targetPair = {numTwo, numsSum};
518+
int insertion_idx = lower_bound(stack.begin(), stack.end(), targetPair) - stack.begin();
519+
520+
if (insertion_idx == stack.size())
521+
stack.insert(stack.begin() + insertion_idx, targetPair);
522+
523+
else if (stack[insertion_idx].second < numsSum)
524+
stack.insert(stack.begin() + insertion_idx, targetPair);
525+
}
526+
527+
pair<int, int> queryNumTwoPair = {queryTwo, 0};
528+
529+
int search_idx = lower_bound(stack.begin(), stack.end(), queryNumTwoPair) - stack.begin();
530+
if (search_idx < stack.size())
531+
maxValues[queryIdx] = stack[search_idx].second;
532+
}
533+
534+
return maxValues;
535+
}
536+
};
537+
```
538+
539+
<!-- tabs:end -->
540+
541+
<!-- solution:end -->
542+
409543
<!-- problem:end -->

solution/2700-2799/2736.Maximum Sum Queries/README_EN.md

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,4 +410,142 @@ function maximumSumQueries(nums1: number[], nums2: number[], queries: number[][]
410410

411411
<!-- solution:end -->
412412

413+
<!-- solution:start -->
414+
415+
### Solution 2: Sorting + Monotonic Stack + Binary Search
416+
417+
We process queries in descending order of their corresponding $x$ threshold.
418+
At the same time, we also sort number pairs in descending order of $\textit{nums1}[i]$.
419+
420+
For each $query_j$, all number pairs satisfying $\textit{nums1}[i] \geq x_j$ are added to a monotonic stack.
421+
422+
Such a stack runs in ascending order of $\textit{nums2}[i]$ but descending order
423+
of $\textit{nums1}[i] + \textit{nums2}[i]$, ensuring that any candidate with a larger $\textit{nums2}[i]$
424+
has a smaller $\textit{nums1}[i] + \textit{nums2}[i]$ instead, so only effective candidates are kept.
425+
426+
For each $query_j$, binary search locates the first stack entry having
427+
$\textit{nums2}[i] \geq y_j$. Its corresponding $\textit{nums1}[i] + \textit{nums2}[i]$ is the answer.
428+
429+
### Time & Space Complexity
430+
431+
Here, $n$ is the length of array $nums2$, and $m$ is the length of array $queries$.
432+
433+
- Time complexity: $O((n + m) \times \log n + m \times \log m)$。
434+
- Space complexity: $O(n + m)$。
435+
436+
<!-- tabs:start -->
437+
438+
#### Python3
439+
440+
```python
441+
class Solution:
442+
def maximumSumQueries(
443+
self, nums1: list[int], nums2: list[int], queries: list[list[int]]
444+
) -> list[int]:
445+
max_values = [-1] * len(queries)
446+
447+
queries = [(query[0], query[1], idx) for idx, query in enumerate(queries)]
448+
# Process queries by descending x threshold and y threshold.
449+
queries.sort(key=lambda x: (-x[0], -x[1]))
450+
451+
tuples: list[tuple[int, int]] = [] # Format: (num 1, num 2).
452+
for num_1, num_2 in zip(nums1, nums2):
453+
tuples.append((num_1, num_2))
454+
455+
# Process queries by descending num 1 and num 2.
456+
# Sort by ascending num 1 and num 2 to pop from the back.
457+
tuples.sort(key=lambda x: (x[0], x[1]))
458+
459+
stack: list[tuple[int, int]] = [] # Format: (num 2, sum).
460+
461+
for query_1, query_2, query_idx in queries:
462+
while tuples and tuples[-1][0] >= query_1: # Tuple's num 1 >= x threshold.
463+
num_1, num_2 = tuples.pop(-1)
464+
nums_sum = num_1 + num_2
465+
466+
while stack and stack[-1][0] < num_2 and stack[-1][1] <= nums_sum:
467+
stack.pop(-1) # Stack top isn't better than popped tuple.
468+
469+
insertion_idx = bisect_left(stack, (num_2, nums_sum))
470+
471+
if insertion_idx == len(stack):
472+
stack.insert(insertion_idx, (num_2, nums_sum))
473+
474+
elif stack[insertion_idx][1] < nums_sum:
475+
stack.insert(insertion_idx, (num_2, nums_sum))
476+
477+
search_idx = bisect_left(stack, (query_2, 0))
478+
if search_idx < len(stack):
479+
max_values[query_idx] = stack[search_idx][1]
480+
481+
return max_values
482+
```
483+
484+
#### C++
485+
486+
```cpp
487+
class Solution {
488+
public:
489+
vector<int> maximumSumQueries(vector<int>& nums1, vector<int>& nums2, vector<vector<int>>& queries) {
490+
vector<int> maxValues(queries.size(), -1);
491+
492+
vector<vector<int>> queriesIndices;
493+
for (int idx = 0; idx < queries.size(); idx++)
494+
queriesIndices.push_back({queries[idx][0], queries[idx][1], idx});
495+
496+
// Process queries by descending x threshold and y threshold.
497+
// Sort ascendingly and later pop from the back.
498+
sort(queriesIndices.begin(), queriesIndices.end());
499+
500+
vector<pair<int, int>> numsPairs; // Format: {num 1, num 2}.
501+
for (int idx = 0; idx < nums2.size(); idx++)
502+
numsPairs.push_back({nums1[idx], nums2[idx]});
503+
504+
// Process queries by descending num 1 and num 2.
505+
// Sort by ascending num 1 and num 2 to pop from the back.
506+
sort(numsPairs.begin(), numsPairs.end());
507+
508+
deque<pair<int, int>> stack; // Format: {num 2, sum}.
509+
510+
while (!queriesIndices.empty()) {
511+
int queryOne = queriesIndices.back()[0];
512+
int queryTwo = queriesIndices.back()[1];
513+
int queryIdx = queriesIndices.back()[2];
514+
queriesIndices.pop_back();
515+
516+
// Pair's num 1 >= x threshold.
517+
while (!numsPairs.empty() && numsPairs.back().first >= queryOne) {
518+
auto [numOne, numTwo] = numsPairs.back();
519+
numsPairs.pop_back();
520+
int numsSum = numOne + numTwo;
521+
522+
while (!stack.empty() and stack.back().first < numTwo and stack.back().second <= numsSum)
523+
stack.pop_back(); // Stack top isn't better than popped pair.
524+
525+
pair<int, int> targetPair = {numTwo, numsSum};
526+
int insertion_idx = lower_bound(stack.begin(), stack.end(), targetPair) - stack.begin();
527+
528+
if (insertion_idx == stack.size())
529+
stack.insert(stack.begin() + insertion_idx, targetPair);
530+
531+
else if (stack[insertion_idx].second < numsSum)
532+
stack.insert(stack.begin() + insertion_idx, targetPair);
533+
}
534+
535+
pair<int, int> queryNumTwoPair = {queryTwo, 0};
536+
537+
int search_idx = lower_bound(stack.begin(), stack.end(), queryNumTwoPair) - stack.begin();
538+
if (search_idx < stack.size())
539+
maxValues[queryIdx] = stack[search_idx].second;
540+
}
541+
542+
return maxValues;
543+
}
544+
};
545+
```
546+
547+
<!-- tabs:end -->
548+
549+
<!-- solution:end -->
550+
413551
<!-- problem:end -->
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
class Solution {
2+
public:
3+
vector<int> maximumSumQueries(vector<int>& nums1, vector<int>& nums2, vector<vector<int>>& queries) {
4+
vector<int> maxValues(queries.size(), -1);
5+
6+
vector<vector<int>> queriesIndices;
7+
for (int idx = 0; idx < queries.size(); idx++)
8+
queriesIndices.push_back({queries[idx][0], queries[idx][1], idx});
9+
10+
// Process queries by descending x threshold and y threshold.
11+
// Sort ascendingly and later pop from the back.
12+
sort(queriesIndices.begin(), queriesIndices.end());
13+
14+
vector<pair<int, int>> numsPairs; // Format: {num 1, num 2}.
15+
for (int idx = 0; idx < nums2.size(); idx++)
16+
numsPairs.push_back({nums1[idx], nums2[idx]});
17+
18+
// Process queries by descending num 1 and num 2.
19+
// Sort by ascending num 1 and num 2 to pop from the back.
20+
sort(numsPairs.begin(), numsPairs.end());
21+
22+
deque<pair<int, int>> stack; // Format: {num 2, sum}.
23+
24+
while (!queriesIndices.empty()) {
25+
int queryOne = queriesIndices.back()[0];
26+
int queryTwo = queriesIndices.back()[1];
27+
int queryIdx = queriesIndices.back()[2];
28+
queriesIndices.pop_back();
29+
30+
// Pair's num 1 >= x threshold.
31+
while (!numsPairs.empty() && numsPairs.back().first >= queryOne) {
32+
auto [numOne, numTwo] = numsPairs.back();
33+
numsPairs.pop_back();
34+
int numsSum = numOne + numTwo;
35+
36+
while (!stack.empty() and stack.back().first < numTwo and stack.back().second <= numsSum)
37+
stack.pop_back(); // Stack top isn't better than popped pair.
38+
39+
pair<int, int> targetPair = {numTwo, numsSum};
40+
int insertion_idx = lower_bound(stack.begin(), stack.end(), targetPair) - stack.begin();
41+
42+
if (insertion_idx == stack.size())
43+
stack.insert(stack.begin() + insertion_idx, targetPair);
44+
45+
else if (stack[insertion_idx].second < numsSum)
46+
stack.insert(stack.begin() + insertion_idx, targetPair);
47+
}
48+
49+
pair<int, int> queryNumTwoPair = {queryTwo, 0};
50+
51+
int search_idx = lower_bound(stack.begin(), stack.end(), queryNumTwoPair) - stack.begin();
52+
if (search_idx < stack.size())
53+
maxValues[queryIdx] = stack[search_idx].second;
54+
}
55+
56+
return maxValues;
57+
}
58+
};
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
class Solution:
2+
def maximumSumQueries(
3+
self, nums1: list[int], nums2: list[int], queries: list[list[int]]
4+
) -> list[int]:
5+
max_values = [-1] * len(queries)
6+
7+
queries = [(query[0], query[1], idx) for idx, query in enumerate(queries)]
8+
# Process queries by descending x threshold and y threshold.
9+
queries.sort(key=lambda x: (-x[0], -x[1]))
10+
11+
tuples: list[tuple[int, int]] = [] # Format: (num 1, num 2).
12+
for num_1, num_2 in zip(nums1, nums2):
13+
tuples.append((num_1, num_2))
14+
15+
# Process queries by descending num 1 and num 2.
16+
# Sort by ascending num 1 and num 2 to pop from the back.
17+
tuples.sort(key=lambda x: (x[0], x[1]))
18+
19+
stack: list[tuple[int, int]] = [] # Format: (num 2, sum).
20+
21+
for query_1, query_2, query_idx in queries:
22+
while tuples and tuples[-1][0] >= query_1: # Tuple's num 1 >= x threshold.
23+
num_1, num_2 = tuples.pop(-1)
24+
nums_sum = num_1 + num_2
25+
26+
while stack and stack[-1][0] < num_2 and stack[-1][1] <= nums_sum:
27+
stack.pop(-1) # Stack top isn't better than popped tuple.
28+
29+
insertion_idx = bisect_left(stack, (num_2, nums_sum))
30+
31+
if insertion_idx == len(stack):
32+
stack.insert(insertion_idx, (num_2, nums_sum))
33+
34+
elif stack[insertion_idx][1] < nums_sum:
35+
stack.insert(insertion_idx, (num_2, nums_sum))
36+
37+
search_idx = bisect_left(stack, (query_2, 0))
38+
if search_idx < len(stack):
39+
max_values[query_idx] = stack[search_idx][1]
40+
41+
return max_values

0 commit comments

Comments
 (0)