|
1 | 1 | package g3601_3700.s3636_threshold_majority_queries;
|
2 | 2 |
|
3 |
| -// #Hard #Biweekly_Contest_162 #2025_08_03_Time_1027_ms_(_%)_Space_72.53_MB_(100.00%) |
| 3 | +// #Hard #Biweekly_Contest_162 #2025_08_06_Time_82_ms_(98.38%)_Space_71.28_MB_(74.76%) |
4 | 4 |
|
5 | 5 | import java.util.ArrayList;
|
6 |
| -import java.util.HashMap; |
| 6 | +import java.util.Arrays; |
7 | 7 | import java.util.List;
|
8 |
| -import java.util.Map; |
9 |
| -import java.util.TreeSet; |
10 | 8 |
|
11 |
| -@SuppressWarnings("java:S1210") |
12 | 9 | public class Solution {
|
| 10 | + private int[] nums; |
| 11 | + private int[] indexToValue; |
| 12 | + private int[] cnt; |
| 13 | + private int maxCnt = 0; |
| 14 | + private int minVal = 0; |
13 | 15 |
|
14 |
| - private static class FreqPair implements Comparable<FreqPair> { |
15 |
| - int count; |
16 |
| - int value; |
17 |
| - |
18 |
| - public FreqPair(int count, int value) { |
19 |
| - this.count = count; |
20 |
| - this.value = value; |
21 |
| - } |
22 |
| - |
23 |
| - @Override |
24 |
| - public int compareTo(FreqPair other) { |
25 |
| - if (this.count != other.count) { |
26 |
| - return Integer.compare(this.count, other.count); |
27 |
| - } |
28 |
| - return Integer.compare(other.value, this.value); |
29 |
| - } |
30 |
| - } |
31 |
| - |
32 |
| - // A helper class to store a query's range and its original index. |
33 | 16 | private static class Query {
|
| 17 | + int bid; |
34 | 18 | int l;
|
35 | 19 | int r;
|
36 |
| - int originalIndex; |
| 20 | + int threshold; |
| 21 | + int qid; |
37 | 22 |
|
38 |
| - public Query(int l, int r, int idx) { |
| 23 | + Query(int bid, int l, int r, int threshold, int qid) { |
| 24 | + this.bid = bid; |
39 | 25 | this.l = l;
|
40 | 26 | this.r = r;
|
41 |
| - this.originalIndex = idx; |
42 |
| - } |
43 |
| - } |
44 |
| - |
45 |
| - private int[] nums; |
46 |
| - private Map<Integer, Integer> counts; |
47 |
| - private TreeSet<FreqPair> sortedFrequencies; |
48 |
| - |
49 |
| - private void add(int pos) { |
50 |
| - int val = this.nums[pos]; |
51 |
| - int oldCount = this.counts.getOrDefault(val, 0); |
52 |
| - if (oldCount > 0) { |
53 |
| - this.sortedFrequencies.remove(new FreqPair(oldCount, val)); |
54 |
| - } |
55 |
| - int newCount = oldCount + 1; |
56 |
| - this.counts.put(val, newCount); |
57 |
| - this.sortedFrequencies.add(new FreqPair(newCount, val)); |
58 |
| - } |
59 |
| - |
60 |
| - private void remove(int pos) { |
61 |
| - int val = this.nums[pos]; |
62 |
| - int oldCount = this.counts.get(val); |
63 |
| - this.sortedFrequencies.remove(new FreqPair(oldCount, val)); |
64 |
| - int newCount = oldCount - 1; |
65 |
| - if (newCount > 0) { |
66 |
| - this.counts.put(val, newCount); |
67 |
| - this.sortedFrequencies.add(new FreqPair(newCount, val)); |
68 |
| - } else { |
69 |
| - this.counts.remove(val); |
| 27 | + this.threshold = threshold; |
| 28 | + this.qid = qid; |
70 | 29 | }
|
71 | 30 | }
|
72 | 31 |
|
73 | 32 | public int[] subarrayMajority(int[] nums, int[][] queries) {
|
74 |
| - this.nums = nums; |
75 |
| - this.counts = new HashMap<>(); |
76 |
| - this.sortedFrequencies = new TreeSet<>(); |
77 | 33 | int n = nums.length;
|
78 |
| - int qLen = queries.length; |
79 |
| - List<Query> queryList = new ArrayList<>(); |
80 |
| - int[] thresholds = new int[qLen]; |
81 |
| - for (int i = 0; i < qLen; i++) { |
82 |
| - queryList.add(new Query(queries[i][0], queries[i][1], i)); |
83 |
| - thresholds[i] = queries[i][2]; |
84 |
| - } |
85 |
| - int blockSize = 1; |
86 |
| - if (qLen > 0) { |
87 |
| - blockSize = Math.max(1, (int) (n / Math.sqrt(qLen))); |
| 34 | + int m = queries.length; |
| 35 | + this.nums = nums; |
| 36 | + cnt = new int[n + 1]; |
| 37 | + int[] nums2 = nums.clone(); |
| 38 | + Arrays.sort(nums2); |
| 39 | + indexToValue = new int[n]; |
| 40 | + for (int i = 0; i < n; i++) { |
| 41 | + indexToValue[i] = Arrays.binarySearch(nums2, nums[i]); |
88 | 42 | }
|
89 |
| - final int finalBlockSize = blockSize; |
90 |
| - queryList.sort( |
91 |
| - (a, b) -> { |
92 |
| - int blockA = a.l / finalBlockSize; |
93 |
| - int blockB = b.l / finalBlockSize; |
94 |
| - if (blockA != blockB) { |
95 |
| - return Integer.compare(blockA, blockB); |
96 |
| - } |
97 |
| - if ((blockA % 2) == 1) { |
98 |
| - return Integer.compare(b.r, a.r); |
99 |
| - } else { |
100 |
| - return Integer.compare(a.r, b.r); |
101 |
| - } |
102 |
| - }); |
103 |
| - |
104 |
| - int[] ans = new int[qLen]; |
105 |
| - int currentL = 0; |
106 |
| - int currentR = -1; |
107 |
| - for (Query q : queryList) { |
108 |
| - while (currentL > q.l) { |
109 |
| - add(--currentL); |
| 43 | + int[] ans = new int[m]; |
| 44 | + int blockSize = (int) Math.ceil(n / Math.sqrt(m)); |
| 45 | + List<Query> qs = new ArrayList<>(); |
| 46 | + for (int i = 0; i < m; i++) { |
| 47 | + int[] q = queries[i]; |
| 48 | + int l = q[0]; |
| 49 | + int r = q[1] + 1; |
| 50 | + int threshold = q[2]; |
| 51 | + if (r - l > blockSize) { |
| 52 | + qs.add(new Query(l / blockSize, l, r, threshold, i)); |
| 53 | + continue; |
110 | 54 | }
|
111 |
| - while (currentR < q.r) { |
112 |
| - add(++currentR); |
| 55 | + for (int j = l; j < r; j++) { |
| 56 | + add(j); |
113 | 57 | }
|
114 |
| - while (currentL < q.l) { |
115 |
| - remove(currentL++); |
| 58 | + ans[i] = maxCnt >= threshold ? minVal : -1; |
| 59 | + for (int j = l; j < r; j++) { |
| 60 | + cnt[indexToValue[j]]--; |
116 | 61 | }
|
117 |
| - while (currentR > q.r) { |
118 |
| - remove(currentR--); |
| 62 | + maxCnt = 0; |
| 63 | + } |
| 64 | + qs.sort((a, b) -> a.bid != b.bid ? a.bid - b.bid : a.r - b.r); |
| 65 | + int r = 0; |
| 66 | + for (int i = 0; i < qs.size(); i++) { |
| 67 | + Query q = qs.get(i); |
| 68 | + int l0 = (q.bid + 1) * blockSize; |
| 69 | + if (i == 0 || q.bid > qs.get(i - 1).bid) { |
| 70 | + r = l0; |
| 71 | + Arrays.fill(cnt, 0); |
| 72 | + maxCnt = 0; |
| 73 | + } |
| 74 | + for (; r < q.r; r++) { |
| 75 | + add(r); |
| 76 | + } |
| 77 | + int tmpMaxCnt = maxCnt; |
| 78 | + int tmpMinVal = minVal; |
| 79 | + for (int j = q.l; j < l0; j++) { |
| 80 | + add(j); |
119 | 81 | }
|
120 |
| - if (sortedFrequencies.isEmpty()) { |
121 |
| - ans[q.originalIndex] = -1; |
122 |
| - } else { |
123 |
| - FreqPair mostFrequent = sortedFrequencies.last(); |
124 |
| - if (mostFrequent.count >= thresholds[q.originalIndex]) { |
125 |
| - ans[q.originalIndex] = mostFrequent.value; |
126 |
| - } else { |
127 |
| - ans[q.originalIndex] = -1; |
128 |
| - } |
| 82 | + ans[q.qid] = maxCnt >= q.threshold ? minVal : -1; |
| 83 | + maxCnt = tmpMaxCnt; |
| 84 | + minVal = tmpMinVal; |
| 85 | + for (int j = q.l; j < l0; j++) { |
| 86 | + cnt[indexToValue[j]]--; |
129 | 87 | }
|
130 | 88 | }
|
131 | 89 | return ans;
|
132 | 90 | }
|
| 91 | + |
| 92 | + private void add(int i) { |
| 93 | + int v = indexToValue[i]; |
| 94 | + int c = ++cnt[v]; |
| 95 | + int x = nums[i]; |
| 96 | + if (c > maxCnt) { |
| 97 | + maxCnt = c; |
| 98 | + minVal = x; |
| 99 | + } else if (c == maxCnt) { |
| 100 | + minVal = Math.min(minVal, x); |
| 101 | + } |
| 102 | + } |
133 | 103 | }
|
0 commit comments