diff --git a/src/main/java/g3501_3600/s3510_minimum_pair_removal_to_sort_array_ii/Solution.java b/src/main/java/g3501_3600/s3510_minimum_pair_removal_to_sort_array_ii/Solution.java index 9cd72b7a9..3f550f47e 100644 --- a/src/main/java/g3501_3600/s3510_minimum_pair_removal_to_sort_array_ii/Solution.java +++ b/src/main/java/g3501_3600/s3510_minimum_pair_removal_to_sort_array_ii/Solution.java @@ -1,104 +1,112 @@ package g3501_3600.s3510_minimum_pair_removal_to_sort_array_ii; // #Hard #Array #Hash_Table #Heap_Priority_Queue #Simulation #Linked_List #Ordered_Set -// #Doubly_Linked_List #2025_04_09_Time_289_ms_(99.58%)_Space_82.88_MB_(17.23%) +// #Doubly_Linked_List #2025_04_29_Time_278_ms_(98.94%)_Space_70.90_MB_(68.88%) -public class Solution { - private static class Segment { - private final int start; - private final int end; - private Segment left; - private Segment right; - private int lIdx; - private long lNum; - private int rIdx; - private long rNum; - private boolean ok; - private long minSum; - private int li; - private int ri; +import java.util.Arrays; - public static Segment init(int[] arr) { - return new Segment(arr, 0, arr.length - 1); +public class Solution { + public int minimumPairRemoval(int[] nums) { + if (nums.length == 1) { + return 0; } - - public Segment(int[] arr, int s, int e) { - start = s; - end = e; - if (s >= e) { - lIdx = rIdx = s; - lNum = rNum = arr[s]; - minSum = Long.MAX_VALUE; - ok = true; - return; + int size = (int) Math.pow(2, Math.ceil(Math.log(nums.length - 1.0) / Math.log(2))); + long[] segment = new long[size * 2 - 1]; + Arrays.fill(segment, Long.MAX_VALUE); + int[] lefts = new int[size * 2 - 1]; + int[] rights = new int[size * 2 - 1]; + long[] sums = new long[nums.length]; + Arrays.fill(sums, Long.MAX_VALUE / 2); + int[][] arrIdxToSegIdx = new int[nums.length][]; + sums[0] = nums[0]; + int count = 0; + arrIdxToSegIdx[0] = new int[] {-1, size - 1}; + for (int i = 1; i < nums.length; i++) { + if (nums[i] < nums[i - 1]) { + count++; } - int mid = s + ((e - s) >> 1); - left = new Segment(arr, s, mid); - right = new Segment(arr, mid + 1, e); - merge(); + lefts[size + i - 2] = i - 1; + rights[size + i - 2] = i; + segment[size + i - 2] = nums[i - 1] + (long) nums[i]; + arrIdxToSegIdx[i] = new int[] {size + i - 2, size + i - 1}; + sums[i] = nums[i]; } - - private void merge() { - lIdx = left.lIdx; - lNum = left.lNum; - rIdx = right.rIdx; - rNum = right.rNum; - ok = left.ok && right.ok && left.rNum <= right.lNum; - minSum = left.minSum; - li = left.li; - ri = left.ri; - if (left.rNum + right.lNum < minSum) { - minSum = left.rNum + right.lNum; - li = left.rIdx; - ri = right.lIdx; - } - if (right.minSum < minSum) { - minSum = right.minSum; - li = right.li; - ri = right.ri; - } + arrIdxToSegIdx[nums.length - 1][1] = -1; + for (int i = size - 2; i >= 0; i--) { + int l = 2 * i + 1; + int r = 2 * i + 2; + segment[i] = Math.min(segment[l], segment[r]); } + return getRes(count, segment, lefts, rights, sums, arrIdxToSegIdx); + } - public void update(int i, long n) { - if (start <= i && end >= i) { - if (start >= end) { - lNum = rNum = n; + private int getRes( + int count, + long[] segment, + int[] lefts, + int[] rights, + long[] sums, + int[][] arrIdxToSegIdx) { + int res = 0; + while (count > 0) { + int segIdx = 0; + while (2 * segIdx + 1 < segment.length) { + int l = 2 * segIdx + 1; + int r = 2 * segIdx + 2; + if (segment[l] <= segment[r]) { + segIdx = l; } else { - left.update(i, n); - right.update(i, n); - merge(); + segIdx = r; } } - } - - public Segment remove(int i) { - if (start > i || end < i) { - return this; - } else if (start >= end) { - return null; + int arrIdxL = lefts[segIdx]; + int arrIdxR = rights[segIdx]; + long numL = sums[arrIdxL]; + long numR = sums[arrIdxR]; + if (numL > numR) { + count--; } - left = left.remove(i); - right = right.remove(i); - if (null == left) { - return right; - } else if (null == right) { - return left; + long newSum = sums[arrIdxL] = sums[arrIdxL] + sums[arrIdxR]; + int[] leftPointer = arrIdxToSegIdx[arrIdxL]; + int[] rightPointer = arrIdxToSegIdx[arrIdxR]; + int prvSegIdx = leftPointer[0]; + int nextSegIdx = rightPointer[1]; + leftPointer[1] = nextSegIdx; + if (prvSegIdx != -1) { + int l = lefts[prvSegIdx]; + if (sums[l] > numL && sums[l] <= newSum) { + count--; + } else if (sums[l] <= numL && sums[l] > newSum) { + count++; + } + modify(segment, prvSegIdx, sums[l] + newSum); + } + if (nextSegIdx != -1) { + int r = rights[nextSegIdx]; + if (numR > sums[r] && newSum <= sums[r]) { + count--; + } else if (numR <= sums[r] && newSum > sums[r]) { + count++; + } + modify(segment, nextSegIdx, newSum + sums[r]); + lefts[nextSegIdx] = arrIdxL; } - merge(); - return this; + modify(segment, segIdx, Long.MAX_VALUE); + res++; } + return res; } - public int minimumPairRemoval(int[] nums) { - Segment root = Segment.init(nums); - int res = 0; - while (!root.ok) { - int l = root.li; - int r = root.ri; - root.update(l, root.minSum); - root = root.remove(r); - res++; + private void modify(long[] segment, int idx, long num) { + if (segment[idx] == num) { + return; + } + segment[idx] = num; + while (idx != 0) { + idx = (idx - 1) / 2; + int l = 2 * idx + 1; + int r = 2 * idx + 2; + segment[idx] = Math.min(segment[l], segment[r]); } - return res; } } diff --git a/src/main/java/g3501_3600/s3515_shortest_path_in_a_weighted_tree/Solution.java b/src/main/java/g3501_3600/s3515_shortest_path_in_a_weighted_tree/Solution.java index a759b6dda..7e4e34890 100644 --- a/src/main/java/g3501_3600/s3515_shortest_path_in_a_weighted_tree/Solution.java +++ b/src/main/java/g3501_3600/s3515_shortest_path_in_a_weighted_tree/Solution.java @@ -1,117 +1,134 @@ package g3501_3600.s3515_shortest_path_in_a_weighted_tree; // #Hard #Array #Depth_First_Search #Tree #Segment_Tree #Binary_Indexed_Tree -// #2025_04_14_Time_38_ms_(100.00%)_Space_146.11_MB_(100.00%) +// #2025_04_29_Time_28_ms_(99.55%)_Space_98.56_MB_(99.77%) import java.util.ArrayList; import java.util.List; @SuppressWarnings("unchecked") public class Solution { - private int[] in; - private int[] out; - private int[] baseDist; - private int[] parent; - private int[] depth; - private int timer = 0; - private int[] edgeWeight; - private List[] adj; - public int[] treeQueries(int n, int[][] edges, int[][] queries) { - adj = new ArrayList[n + 1]; + // store the queries input midway as requested + int[][] jalkimoren = queries; + // build adjacency list with edge‐indices + List[] adj = new ArrayList[n + 1]; for (int i = 1; i <= n; i++) { adj[i] = new ArrayList<>(); } - for (int[] e : edges) { - int u = e[0]; - int v = e[1]; - int w = e[2]; - adj[u].add(new int[] {v, w}); - adj[v].add(new int[] {u, w}); + for (int i = 0; i < n - 1; i++) { + int u = edges[i][0]; + int v = edges[i][1]; + int w = edges[i][2]; + adj[u].add(new Edge(v, w, i)); + adj[v].add(new Edge(u, w, i)); + } + // parent, Euler‐tour times, depth‐sum, and mapping node→edge‐index + int[] parent = new int[n + 1]; + int[] tin = new int[n + 1]; + int[] tout = new int[n + 1]; + int[] depthSum = new int[n + 1]; + int[] edgeIndexForNode = new int[n + 1]; + int[] weights = new int[n - 1]; + for (int i = 0; i < n - 1; i++) { + weights[i] = edges[i][2]; } - in = new int[n + 1]; - out = new int[n + 1]; - baseDist = new int[n + 1]; - parent = new int[n + 1]; - depth = new int[n + 1]; - edgeWeight = new int[n + 1]; - dfs(1, 0, 0); - Fen fenw = new Fen(n); - List ansList = new ArrayList<>(); - for (int[] query : queries) { - if (query[0] == 1) { - int u = query[1]; - int v = query[2]; - int newW = query[3]; - int child; - if (parent[v] == u) { - child = v; - } else if (parent[u] == v) { - child = u; - } else { + // iterative DFS to compute tin/tout, parent[], depthSum[], edgeIndexForNode[] + int time = 0; + int[] stack = new int[n]; + int[] ptr = new int[n + 1]; + int sp = 0; + stack[sp++] = 1; + while (sp > 0) { + int u = stack[sp - 1]; + if (ptr[u] == 0) { + tin[u] = ++time; + } + if (ptr[u] < adj[u].size()) { + Edge e = adj[u].get(ptr[u]++); + int v = e.to; + if (v == parent[u]) { continue; } - int diff = newW - edgeWeight[child]; - edgeWeight[child] = newW; - fenw.updateRange(in[child], out[child], diff); + parent[v] = u; + depthSum[v] = depthSum[u] + e.w; + edgeIndexForNode[v] = e.idx; + stack[sp++] = v; } else { - int x = query[1]; - int delta = fenw.query(in[x]); - ansList.add(baseDist[x] + delta); + tout[u] = time; + sp--; } } - int[] answer = new int[ansList.size()]; - for (int i = 0; i < ansList.size(); i++) { - answer[i] = ansList.get(i); + // Fenwick tree for range‐add / point‐query on Euler‐tour array + Fenwick bit = new Fenwick(n + 2); + List answers = new ArrayList<>(); + // process queries + for (int[] q : jalkimoren) { + if (q[0] == 1) { + // update edge weight + int u = q[1]; + int v = q[2]; + int newW = q[3]; + int child = (parent[u] == v) ? u : v; + int idx = edgeIndexForNode[child]; + int delta = newW - weights[idx]; + if (delta != 0) { + weights[idx] = newW; + bit.rangeAdd(tin[child], tout[child], delta); + } + } else { + // query root→x distance + int x = q[1]; + answers.add(depthSum[x] + bit.pointQuery(tin[x])); + } + } + // pack results into array + int m = answers.size(); + int[] ansArr = new int[m]; + for (int i = 0; i < m; i++) { + ansArr[i] = answers.get(i); } - return answer; + return ansArr; } - private void dfs(int node, int par, int dist) { - parent[node] = par; - baseDist[node] = dist; - depth[node] = (par == 0) ? 0 : depth[par] + 1; - in[node] = ++timer; - for (int[] neighborInfo : adj[node]) { - int neighbor = neighborInfo[0]; - int w = neighborInfo[1]; - if (neighbor == par) { - continue; - } - edgeWeight[neighbor] = w; - dfs(neighbor, node, dist + w); + private static class Edge { + int to; + int w; + int idx; + + Edge(int to, int w, int idx) { + this.to = to; + this.w = w; + this.idx = idx; } - out[node] = timer; } - private static class Fen { + private static class Fenwick { int n; - int[] fenw; + int[] f; - public Fen(int n) { + Fenwick(int n) { this.n = n; - fenw = new int[n + 2]; + f = new int[n]; } - private void update(int i, int delta) { - while (i <= n) { - fenw[i] += delta; - i += i & -i; + void update(int i, int v) { + for (; i < n; i += i & -i) { + f[i] += v; } } - public void updateRange(int l, int r, int delta) { - update(l, delta); - update(r + 1, -delta); + void rangeAdd(int l, int r, int v) { + update(l, v); + update(r + 1, -v); } - public int query(int i) { - int sum = 0; - while (i > 0) { - sum += fenw[i]; - i -= i & -i; + int pointQuery(int i) { + int s = 0; + for (; i > 0; i -= i & -i) { + s += f[i]; } - return sum; + return s; } } } diff --git a/src/test/java/g3501_3600/s3510_minimum_pair_removal_to_sort_array_ii/SolutionTest.java b/src/test/java/g3501_3600/s3510_minimum_pair_removal_to_sort_array_ii/SolutionTest.java index c6fde4200..f210f218c 100644 --- a/src/test/java/g3501_3600/s3510_minimum_pair_removal_to_sort_array_ii/SolutionTest.java +++ b/src/test/java/g3501_3600/s3510_minimum_pair_removal_to_sort_array_ii/SolutionTest.java @@ -15,4 +15,21 @@ void minimumPairRemoval() { void minimumPairRemoval2() { assertThat(new Solution().minimumPairRemoval(new int[] {1, 2, 2}), equalTo(0)); } + + @Test + void minimumPairRemoval3() { + assertThat(new Solution().minimumPairRemoval(new int[] {5, 2, 3, 1}), equalTo(2)); + } + + @Test + void minimumPairRemoval4() { + assertThat( + new Solution().minimumPairRemoval(new int[] {2, 2, -1, 3, -2, 2, 1, 1, 1, 0, -1}), + equalTo(9)); + } + + @Test + void minimumPairRemoval5() { + assertThat(new Solution().minimumPairRemoval(new int[] {5}), equalTo(0)); + } }