|
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