|
1 | 1 | package g3501_3600.s3510_minimum_pair_removal_to_sort_array_ii;
|
2 | 2 |
|
3 | 3 | // #Hard #Array #Hash_Table #Heap_Priority_Queue #Simulation #Linked_List #Ordered_Set
|
4 |
| -// #Doubly_Linked_List #2025_04_09_Time_289_ms_(99.58%)_Space_82.88_MB_(17.23%) |
| 4 | +// #Doubly_Linked_List #2025_04_29_Time_278_ms_(98.94%)_Space_70.90_MB_(68.88%) |
5 | 5 |
|
6 |
| -public class Solution { |
7 |
| - private static class Segment { |
8 |
| - private final int start; |
9 |
| - private final int end; |
10 |
| - private Segment left; |
11 |
| - private Segment right; |
12 |
| - private int lIdx; |
13 |
| - private long lNum; |
14 |
| - private int rIdx; |
15 |
| - private long rNum; |
16 |
| - private boolean ok; |
17 |
| - private long minSum; |
18 |
| - private int li; |
19 |
| - private int ri; |
| 6 | +import java.util.Arrays; |
20 | 7 |
|
21 |
| - public static Segment init(int[] arr) { |
22 |
| - return new Segment(arr, 0, arr.length - 1); |
| 8 | +public class Solution { |
| 9 | + public int minimumPairRemoval(int[] nums) { |
| 10 | + if (nums.length == 1) { |
| 11 | + return 0; |
23 | 12 | }
|
24 |
| - |
25 |
| - public Segment(int[] arr, int s, int e) { |
26 |
| - start = s; |
27 |
| - end = e; |
28 |
| - if (s >= e) { |
29 |
| - lIdx = rIdx = s; |
30 |
| - lNum = rNum = arr[s]; |
31 |
| - minSum = Long.MAX_VALUE; |
32 |
| - ok = true; |
33 |
| - return; |
| 13 | + int size = (int) Math.pow(2, Math.ceil(Math.log(nums.length - 1.0) / Math.log(2))); |
| 14 | + long[] segment = new long[size * 2 - 1]; |
| 15 | + Arrays.fill(segment, Long.MAX_VALUE); |
| 16 | + int[] lefts = new int[size * 2 - 1]; |
| 17 | + int[] rights = new int[size * 2 - 1]; |
| 18 | + long[] sums = new long[nums.length]; |
| 19 | + Arrays.fill(sums, Long.MAX_VALUE / 2); |
| 20 | + int[][] arrIdxToSegIdx = new int[nums.length][]; |
| 21 | + sums[0] = nums[0]; |
| 22 | + int count = 0; |
| 23 | + arrIdxToSegIdx[0] = new int[] {-1, size - 1}; |
| 24 | + for (int i = 1; i < nums.length; i++) { |
| 25 | + if (nums[i] < nums[i - 1]) { |
| 26 | + count++; |
34 | 27 | }
|
35 |
| - int mid = s + ((e - s) >> 1); |
36 |
| - left = new Segment(arr, s, mid); |
37 |
| - right = new Segment(arr, mid + 1, e); |
38 |
| - merge(); |
| 28 | + lefts[size + i - 2] = i - 1; |
| 29 | + rights[size + i - 2] = i; |
| 30 | + segment[size + i - 2] = nums[i - 1] + (long) nums[i]; |
| 31 | + arrIdxToSegIdx[i] = new int[] {size + i - 2, size + i - 1}; |
| 32 | + sums[i] = nums[i]; |
39 | 33 | }
|
40 |
| - |
41 |
| - private void merge() { |
42 |
| - lIdx = left.lIdx; |
43 |
| - lNum = left.lNum; |
44 |
| - rIdx = right.rIdx; |
45 |
| - rNum = right.rNum; |
46 |
| - ok = left.ok && right.ok && left.rNum <= right.lNum; |
47 |
| - minSum = left.minSum; |
48 |
| - li = left.li; |
49 |
| - ri = left.ri; |
50 |
| - if (left.rNum + right.lNum < minSum) { |
51 |
| - minSum = left.rNum + right.lNum; |
52 |
| - li = left.rIdx; |
53 |
| - ri = right.lIdx; |
54 |
| - } |
55 |
| - if (right.minSum < minSum) { |
56 |
| - minSum = right.minSum; |
57 |
| - li = right.li; |
58 |
| - ri = right.ri; |
59 |
| - } |
| 34 | + arrIdxToSegIdx[nums.length - 1][1] = -1; |
| 35 | + for (int i = size - 2; i >= 0; i--) { |
| 36 | + int l = 2 * i + 1; |
| 37 | + int r = 2 * i + 2; |
| 38 | + segment[i] = Math.min(segment[l], segment[r]); |
60 | 39 | }
|
| 40 | + return getRes(count, segment, lefts, rights, sums, arrIdxToSegIdx); |
| 41 | + } |
61 | 42 |
|
62 |
| - public void update(int i, long n) { |
63 |
| - if (start <= i && end >= i) { |
64 |
| - if (start >= end) { |
65 |
| - lNum = rNum = n; |
| 43 | + private int getRes( |
| 44 | + int count, |
| 45 | + long[] segment, |
| 46 | + int[] lefts, |
| 47 | + int[] rights, |
| 48 | + long[] sums, |
| 49 | + int[][] arrIdxToSegIdx) { |
| 50 | + int res = 0; |
| 51 | + while (count > 0) { |
| 52 | + int segIdx = 0; |
| 53 | + while (2 * segIdx + 1 < segment.length) { |
| 54 | + int l = 2 * segIdx + 1; |
| 55 | + int r = 2 * segIdx + 2; |
| 56 | + if (segment[l] <= segment[r]) { |
| 57 | + segIdx = l; |
66 | 58 | } else {
|
67 |
| - left.update(i, n); |
68 |
| - right.update(i, n); |
69 |
| - merge(); |
| 59 | + segIdx = r; |
70 | 60 | }
|
71 | 61 | }
|
72 |
| - } |
73 |
| - |
74 |
| - public Segment remove(int i) { |
75 |
| - if (start > i || end < i) { |
76 |
| - return this; |
77 |
| - } else if (start >= end) { |
78 |
| - return null; |
| 62 | + int arrIdxL = lefts[segIdx]; |
| 63 | + int arrIdxR = rights[segIdx]; |
| 64 | + long numL = sums[arrIdxL]; |
| 65 | + long numR = sums[arrIdxR]; |
| 66 | + if (numL > numR) { |
| 67 | + count--; |
79 | 68 | }
|
80 |
| - left = left.remove(i); |
81 |
| - right = right.remove(i); |
82 |
| - if (null == left) { |
83 |
| - return right; |
84 |
| - } else if (null == right) { |
85 |
| - return left; |
| 69 | + long newSum = sums[arrIdxL] = sums[arrIdxL] + sums[arrIdxR]; |
| 70 | + int[] leftPointer = arrIdxToSegIdx[arrIdxL]; |
| 71 | + int[] rightPointer = arrIdxToSegIdx[arrIdxR]; |
| 72 | + int prvSegIdx = leftPointer[0]; |
| 73 | + int nextSegIdx = rightPointer[1]; |
| 74 | + leftPointer[1] = nextSegIdx; |
| 75 | + if (prvSegIdx != -1) { |
| 76 | + int l = lefts[prvSegIdx]; |
| 77 | + if (sums[l] > numL && sums[l] <= newSum) { |
| 78 | + count--; |
| 79 | + } else if (sums[l] <= numL && sums[l] > newSum) { |
| 80 | + count++; |
| 81 | + } |
| 82 | + modify(segment, prvSegIdx, sums[l] + newSum); |
| 83 | + } |
| 84 | + if (nextSegIdx != -1) { |
| 85 | + int r = rights[nextSegIdx]; |
| 86 | + if (numR > sums[r] && newSum <= sums[r]) { |
| 87 | + count--; |
| 88 | + } else if (numR <= sums[r] && newSum > sums[r]) { |
| 89 | + count++; |
| 90 | + } |
| 91 | + modify(segment, nextSegIdx, newSum + sums[r]); |
| 92 | + lefts[nextSegIdx] = arrIdxL; |
86 | 93 | }
|
87 |
| - merge(); |
88 |
| - return this; |
| 94 | + modify(segment, segIdx, Long.MAX_VALUE); |
| 95 | + res++; |
89 | 96 | }
|
| 97 | + return res; |
90 | 98 | }
|
91 | 99 |
|
92 |
| - public int minimumPairRemoval(int[] nums) { |
93 |
| - Segment root = Segment.init(nums); |
94 |
| - int res = 0; |
95 |
| - while (!root.ok) { |
96 |
| - int l = root.li; |
97 |
| - int r = root.ri; |
98 |
| - root.update(l, root.minSum); |
99 |
| - root = root.remove(r); |
100 |
| - res++; |
| 100 | + private void modify(long[] segment, int idx, long num) { |
| 101 | + if (segment[idx] == num) { |
| 102 | + return; |
| 103 | + } |
| 104 | + segment[idx] = num; |
| 105 | + while (idx != 0) { |
| 106 | + idx = (idx - 1) / 2; |
| 107 | + int l = 2 * idx + 1; |
| 108 | + int r = 2 * idx + 2; |
| 109 | + segment[idx] = Math.min(segment[l], segment[r]); |
101 | 110 | }
|
102 |
| - return res; |
103 | 111 | }
|
104 | 112 | }
|
0 commit comments