diff --git a/src/main/kotlin/g3501_3600/s3510_minimum_pair_removal_to_sort_array_ii/Solution.kt b/src/main/kotlin/g3501_3600/s3510_minimum_pair_removal_to_sort_array_ii/Solution.kt index f95546086..eb40645c8 100644 --- a/src/main/kotlin/g3501_3600/s3510_minimum_pair_removal_to_sort_array_ii/Solution.kt +++ b/src/main/kotlin/g3501_3600/s3510_minimum_pair_removal_to_sort_array_ii/Solution.kt @@ -1,113 +1,119 @@ 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_219_ms_(100.00%)_Space_108.86_MB_(9.09%) +// #Doubly_Linked_List #2025_04_29_Time_172_ms_(100.00%)_Space_85.64_MB_(66.67%) -class Solution { - private class Segment { - private val start: Int - private val end: Int - private var left: Segment? = null - private var right: Segment? = null - private var lIdx: Int = 0 - private var lNum: Long = 0 - private var rIdx: Int = 0 - private var rNum: Long = 0 - var ok: Boolean = false - var minSum: Long = 0 - var li: Int = 0 - var ri: Int = 0 +import kotlin.math.ceil +import kotlin.math.ln +import kotlin.math.min +import kotlin.math.pow - companion object { - fun init(arr: IntArray): Segment { - return Segment(arr, 0, arr.size - 1) - } +class Solution { + fun minimumPairRemoval(nums: IntArray): Int { + if (nums.size == 1) { + return 0 } - - constructor(arr: IntArray, s: Int, e: Int) { - start = s - end = e - if (s >= e) { - lIdx = s - rIdx = s - lNum = arr[s].toLong() - rNum = arr[s].toLong() - minSum = Long.MAX_VALUE - ok = true - return + val size = 2.0.pow(ceil(ln(nums.size - 1.0) / ln(2.0))).toInt() + val segment = LongArray(size * 2 - 1) + segment.fill(Long.Companion.MAX_VALUE) + val lefts = IntArray(size * 2 - 1) + val rights = IntArray(size * 2 - 1) + val sums = LongArray(nums.size) + sums.fill(Long.Companion.MAX_VALUE / 2) + val arrIdxToSegIdx: Array = Array(nums.size) { IntArray(0) } + sums[0] = nums[0].toLong() + var count = 0 + arrIdxToSegIdx[0] = intArrayOf(-1, size - 1) + for (i in 1.. - right?.let { right -> - 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.size - 1][1] = -1 + for (i in size - 2 downTo 0) { + val l = 2 * i + 1 + val r = 2 * i + 2 + segment[i] = min(segment[l], segment[r]) } + return getRes(count, segment, lefts, rights, sums, arrIdxToSegIdx) + } - fun update(i: Int, n: Long) { - if (start <= i && end >= i) { - if (start >= end) { - lNum = n - rNum = n + private fun getRes( + count: Int, + segment: LongArray, + lefts: IntArray, + rights: IntArray, + sums: LongArray, + arrIdxToSegIdx: Array, + ): Int { + var count = count + var res = 0 + while (count > 0) { + var segIdx = 0 + while (2 * segIdx + 1 < segment.size) { + val l = 2 * segIdx + 1 + val r = 2 * segIdx + 2 + segIdx = if (segment[l] <= segment[r]) { + l } else { - left?.update(i, n) - right?.update(i, n) - merge() + r } } - } - - fun remove(i: Int): Segment? { - if (start > i || end < i) { - return this - } else if (start >= end) { - return null + val arrIdxL = lefts[segIdx] + val arrIdxR = rights[segIdx] + val numL = sums[arrIdxL] + val numR = sums[arrIdxR] + if (numL > numR) { + count-- + } + sums[arrIdxL] = sums[arrIdxL] + sums[arrIdxR] + val newSum = sums[arrIdxL] + val leftPointer = arrIdxToSegIdx[arrIdxL] + val rightPointer = arrIdxToSegIdx[arrIdxR] + val prvSegIdx = leftPointer[0] + val nextSegIdx = rightPointer[1] + leftPointer[1] = nextSegIdx + if (prvSegIdx != -1) { + val 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) } - left = left?.remove(i) - right = right?.remove(i) - if (left == null) { - return right - } else if (right == null) { - return left + if (nextSegIdx != -1) { + val 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.Companion.MAX_VALUE) + res++ } + return res } - fun minimumPairRemoval(nums: IntArray): Int { - var root = Segment.init(nums) - var res = 0 - while (!root.ok) { - val l = root.li - val r = root.ri - root.update(l, root.minSum) - root = root.remove(r) ?: break - res++ + private fun modify(segment: LongArray, idx: Int, num: Long) { + var idx = idx + if (segment[idx] == num) { + return + } + segment[idx] = num + while (idx != 0) { + idx = (idx - 1) / 2 + val l = 2 * idx + 1 + val r = 2 * idx + 2 + segment[idx] = min(segment[l], segment[r]) } - return res } } diff --git a/src/main/kotlin/g3501_3600/s3515_shortest_path_in_a_weighted_tree/Solution.kt b/src/main/kotlin/g3501_3600/s3515_shortest_path_in_a_weighted_tree/Solution.kt index 653c340b5..b6a768fae 100644 --- a/src/main/kotlin/g3501_3600/s3515_shortest_path_in_a_weighted_tree/Solution.kt +++ b/src/main/kotlin/g3501_3600/s3515_shortest_path_in_a_weighted_tree/Solution.kt @@ -1,106 +1,115 @@ 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_65_ms_(100.00%)_Space_179.96_MB_(100.00%) +// #2025_04_29_Time_45_ms_(100.00%)_Space_134.07_MB_(100.00%) class Solution { - private lateinit var `in`: IntArray - private lateinit var out: IntArray - private lateinit var baseDist: IntArray - private lateinit var parent: IntArray - private lateinit var depth: IntArray - private var timer = 0 - private lateinit var edgeWeight: IntArray - private lateinit var adj: Array> - fun treeQueries(n: Int, edges: Array, queries: Array): IntArray { - adj = Array>(n + 1) { ArrayList() } - for (e in edges) { - val u = e[0] - val v = e[1] - val w = e[2] - adj[u].add(intArrayOf(v, w)) - adj[v].add(intArrayOf(u, w)) + // store the queries input midway as requested + val jalkimoren = queries + // build adjacency list with edge‐indices + val adj: Array> = Array(n + 1) { ArrayList() } + for (i in 0.. = ArrayList() - for (query in queries) { - if (query[0] == 1) { - val u = query[1] - val v = query[2] - val newW = query[3] - val child: Int - if (parent[v] == u) { - child = v - } else if (parent[u] == v) { - child = u - } else { + // iterative DFS to compute tin/tout, parent[], depthSum[], edgeIndexForNode[] + var time = 0 + val stack = IntArray(n) + val ptr = IntArray(n + 1) + var sp = 0 + stack[sp++] = 1 + while (sp > 0) { + val u = stack[sp - 1] + if (ptr[u] == 0) { + tin[u] = ++time + } + if (ptr[u] < adj[u].size) { + val e = adj[u][ptr[u]++] + val v = e.to + if (v == parent[u]) { continue } - val 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 { - val x = query[1] - val delta = fenw.query(`in`[x]) - ansList.add(baseDist[x] + delta) + tout[u] = time + sp-- } } - val answer = IntArray(ansList.size) - for (i in ansList.indices) { - answer[i] = ansList[i] - } - return answer - } - - private fun dfs(node: Int, par: Int, dist: Int) { - parent[node] = par - baseDist[node] = dist - depth[node] = if (par == 0) 0 else depth[par] + 1 - `in`[node] = ++timer - for (neighborInfo in adj[node]) { - val neighbor = neighborInfo[0] - val w = neighborInfo[1] - if (neighbor == par) { - continue + // Fenwick tree for range‐add / point‐query on Euler‐tour array + val bit = Fenwick(n + 2) + val answers: MutableList = ArrayList() + // process queries + for (q in jalkimoren) { + if (q[0] == 1) { + // update edge weight + val u = q[1] + val v = q[2] + val newW = q[3] + val child = if (parent[u] == v) u else v + val idx = edgeIndexForNode[child] + val delta = newW - weights[idx] + if (delta != 0) { + weights[idx] = newW + bit.rangeAdd(tin[child], tout[child], delta) + } + } else { + // query root→x distance + val x = q[1] + answers.add(depthSum[x] + bit.pointQuery(tin[x])) } - edgeWeight[neighbor] = w - dfs(neighbor, node, dist + w) } - out[node] = timer + // pack results into array + val m = answers.size + val ansArr = IntArray(m) + for (i in 0.. 0) { - sum += fenw[i] + s += f[i] i -= i and -i } - return sum + return s } } } diff --git a/src/test/kotlin/g3501_3600/s3510_minimum_pair_removal_to_sort_array_ii/SolutionTest.kt b/src/test/kotlin/g3501_3600/s3510_minimum_pair_removal_to_sort_array_ii/SolutionTest.kt index ca54e4afc..b7b3e0f80 100644 --- a/src/test/kotlin/g3501_3600/s3510_minimum_pair_removal_to_sort_array_ii/SolutionTest.kt +++ b/src/test/kotlin/g3501_3600/s3510_minimum_pair_removal_to_sort_array_ii/SolutionTest.kt @@ -20,4 +20,22 @@ internal class SolutionTest { equalTo(0), ) } + + @Test + fun minimumPairRemoval3() { + assertThat(Solution().minimumPairRemoval(intArrayOf(5, 2, 3, 1)), equalTo(2)) + } + + @Test + fun minimumPairRemoval4() { + assertThat( + Solution().minimumPairRemoval(intArrayOf(2, 2, -1, 3, -2, 2, 1, 1, 1, 0, -1)), + equalTo(9), + ) + } + + @Test + fun minimumPairRemoval5() { + assertThat(Solution().minimumPairRemoval(intArrayOf(5)), equalTo(0)) + } }