|  | 
| 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_219_ms_(100.00%)_Space_108.86_MB_(9.09%) | 
|  | 4 | +// #Doubly_Linked_List #2025_04_29_Time_172_ms_(100.00%)_Space_85.64_MB_(66.67%) | 
| 5 | 5 | 
 | 
| 6 |  | -class Solution { | 
| 7 |  | -    private class Segment { | 
| 8 |  | -        private val start: Int | 
| 9 |  | -        private val end: Int | 
| 10 |  | -        private var left: Segment? = null | 
| 11 |  | -        private var right: Segment? = null | 
| 12 |  | -        private var lIdx: Int = 0 | 
| 13 |  | -        private var lNum: Long = 0 | 
| 14 |  | -        private var rIdx: Int = 0 | 
| 15 |  | -        private var rNum: Long = 0 | 
| 16 |  | -        var ok: Boolean = false | 
| 17 |  | -        var minSum: Long = 0 | 
| 18 |  | -        var li: Int = 0 | 
| 19 |  | -        var ri: Int = 0 | 
|  | 6 | +import kotlin.math.ceil | 
|  | 7 | +import kotlin.math.ln | 
|  | 8 | +import kotlin.math.min | 
|  | 9 | +import kotlin.math.pow | 
| 20 | 10 | 
 | 
| 21 |  | -        companion object { | 
| 22 |  | -            fun init(arr: IntArray): Segment { | 
| 23 |  | -                return Segment(arr, 0, arr.size - 1) | 
| 24 |  | -            } | 
|  | 11 | +class Solution { | 
|  | 12 | +    fun minimumPairRemoval(nums: IntArray): Int { | 
|  | 13 | +        if (nums.size == 1) { | 
|  | 14 | +            return 0 | 
| 25 | 15 |         } | 
| 26 |  | - | 
| 27 |  | -        constructor(arr: IntArray, s: Int, e: Int) { | 
| 28 |  | -            start = s | 
| 29 |  | -            end = e | 
| 30 |  | -            if (s >= e) { | 
| 31 |  | -                lIdx = s | 
| 32 |  | -                rIdx = s | 
| 33 |  | -                lNum = arr[s].toLong() | 
| 34 |  | -                rNum = arr[s].toLong() | 
| 35 |  | -                minSum = Long.MAX_VALUE | 
| 36 |  | -                ok = true | 
| 37 |  | -                return | 
|  | 16 | +        val size = 2.0.pow(ceil(ln(nums.size - 1.0) / ln(2.0))).toInt() | 
|  | 17 | +        val segment = LongArray(size * 2 - 1) | 
|  | 18 | +        segment.fill(Long.Companion.MAX_VALUE) | 
|  | 19 | +        val lefts = IntArray(size * 2 - 1) | 
|  | 20 | +        val rights = IntArray(size * 2 - 1) | 
|  | 21 | +        val sums = LongArray(nums.size) | 
|  | 22 | +        sums.fill(Long.Companion.MAX_VALUE / 2) | 
|  | 23 | +        val arrIdxToSegIdx: Array<IntArray> = Array(nums.size) { IntArray(0) } | 
|  | 24 | +        sums[0] = nums[0].toLong() | 
|  | 25 | +        var count = 0 | 
|  | 26 | +        arrIdxToSegIdx[0] = intArrayOf(-1, size - 1) | 
|  | 27 | +        for (i in 1..<nums.size) { | 
|  | 28 | +            if (nums[i] < nums[i - 1]) { | 
|  | 29 | +                count++ | 
| 38 | 30 |             } | 
| 39 |  | -            val mid = s + ((e - s) shr 1) | 
| 40 |  | -            left = Segment(arr, s, mid) | 
| 41 |  | -            right = Segment(arr, mid + 1, e) | 
| 42 |  | -            merge() | 
|  | 31 | +            lefts[size + i - 2] = i - 1 | 
|  | 32 | +            rights[size + i - 2] = i | 
|  | 33 | +            segment[size + i - 2] = nums[i - 1] + nums[i].toLong() | 
|  | 34 | +            arrIdxToSegIdx[i] = intArrayOf(size + i - 2, size + i - 1) | 
|  | 35 | +            sums[i] = nums[i].toLong() | 
| 43 | 36 |         } | 
| 44 |  | - | 
| 45 |  | -        private fun merge() { | 
| 46 |  | -            left?.let { left -> | 
| 47 |  | -                right?.let { right -> | 
| 48 |  | -                    lIdx = left.lIdx | 
| 49 |  | -                    lNum = left.lNum | 
| 50 |  | -                    rIdx = right.rIdx | 
| 51 |  | -                    rNum = right.rNum | 
| 52 |  | -                    ok = left.ok && right.ok && left.rNum <= right.lNum | 
| 53 |  | -                    minSum = left.minSum | 
| 54 |  | -                    li = left.li | 
| 55 |  | -                    ri = left.ri | 
| 56 |  | -                    if (left.rNum + right.lNum < minSum) { | 
| 57 |  | -                        minSum = left.rNum + right.lNum | 
| 58 |  | -                        li = left.rIdx | 
| 59 |  | -                        ri = right.lIdx | 
| 60 |  | -                    } | 
| 61 |  | -                    if (right.minSum < minSum) { | 
| 62 |  | -                        minSum = right.minSum | 
| 63 |  | -                        li = right.li | 
| 64 |  | -                        ri = right.ri | 
| 65 |  | -                    } | 
| 66 |  | -                } | 
| 67 |  | -            } | 
|  | 37 | +        arrIdxToSegIdx[nums.size - 1][1] = -1 | 
|  | 38 | +        for (i in size - 2 downTo 0) { | 
|  | 39 | +            val l = 2 * i + 1 | 
|  | 40 | +            val r = 2 * i + 2 | 
|  | 41 | +            segment[i] = min(segment[l], segment[r]) | 
| 68 | 42 |         } | 
|  | 43 | +        return getRes(count, segment, lefts, rights, sums, arrIdxToSegIdx) | 
|  | 44 | +    } | 
| 69 | 45 | 
 | 
| 70 |  | -        fun update(i: Int, n: Long) { | 
| 71 |  | -            if (start <= i && end >= i) { | 
| 72 |  | -                if (start >= end) { | 
| 73 |  | -                    lNum = n | 
| 74 |  | -                    rNum = n | 
|  | 46 | +    private fun getRes( | 
|  | 47 | +        count: Int, | 
|  | 48 | +        segment: LongArray, | 
|  | 49 | +        lefts: IntArray, | 
|  | 50 | +        rights: IntArray, | 
|  | 51 | +        sums: LongArray, | 
|  | 52 | +        arrIdxToSegIdx: Array<IntArray>, | 
|  | 53 | +    ): Int { | 
|  | 54 | +        var count = count | 
|  | 55 | +        var res = 0 | 
|  | 56 | +        while (count > 0) { | 
|  | 57 | +            var segIdx = 0 | 
|  | 58 | +            while (2 * segIdx + 1 < segment.size) { | 
|  | 59 | +                val l = 2 * segIdx + 1 | 
|  | 60 | +                val r = 2 * segIdx + 2 | 
|  | 61 | +                segIdx = if (segment[l] <= segment[r]) { | 
|  | 62 | +                    l | 
| 75 | 63 |                 } else { | 
| 76 |  | -                    left?.update(i, n) | 
| 77 |  | -                    right?.update(i, n) | 
| 78 |  | -                    merge() | 
|  | 64 | +                    r | 
| 79 | 65 |                 } | 
| 80 | 66 |             } | 
| 81 |  | -        } | 
| 82 |  | - | 
| 83 |  | -        fun remove(i: Int): Segment? { | 
| 84 |  | -            if (start > i || end < i) { | 
| 85 |  | -                return this | 
| 86 |  | -            } else if (start >= end) { | 
| 87 |  | -                return null | 
|  | 67 | +            val arrIdxL = lefts[segIdx] | 
|  | 68 | +            val arrIdxR = rights[segIdx] | 
|  | 69 | +            val numL = sums[arrIdxL] | 
|  | 70 | +            val numR = sums[arrIdxR] | 
|  | 71 | +            if (numL > numR) { | 
|  | 72 | +                count-- | 
|  | 73 | +            } | 
|  | 74 | +            sums[arrIdxL] = sums[arrIdxL] + sums[arrIdxR] | 
|  | 75 | +            val newSum = sums[arrIdxL] | 
|  | 76 | +            val leftPointer = arrIdxToSegIdx[arrIdxL] | 
|  | 77 | +            val rightPointer = arrIdxToSegIdx[arrIdxR] | 
|  | 78 | +            val prvSegIdx = leftPointer[0] | 
|  | 79 | +            val nextSegIdx = rightPointer[1] | 
|  | 80 | +            leftPointer[1] = nextSegIdx | 
|  | 81 | +            if (prvSegIdx != -1) { | 
|  | 82 | +                val l = lefts[prvSegIdx] | 
|  | 83 | +                if (sums[l] > numL && sums[l] <= newSum) { | 
|  | 84 | +                    count-- | 
|  | 85 | +                } else if (sums[l] <= numL && sums[l] > newSum) { | 
|  | 86 | +                    count++ | 
|  | 87 | +                } | 
|  | 88 | +                modify(segment, prvSegIdx, sums[l] + newSum) | 
| 88 | 89 |             } | 
| 89 |  | -            left = left?.remove(i) | 
| 90 |  | -            right = right?.remove(i) | 
| 91 |  | -            if (left == null) { | 
| 92 |  | -                return right | 
| 93 |  | -            } else if (right == null) { | 
| 94 |  | -                return left | 
|  | 90 | +            if (nextSegIdx != -1) { | 
|  | 91 | +                val r = rights[nextSegIdx] | 
|  | 92 | +                if (numR > sums[r] && newSum <= sums[r]) { | 
|  | 93 | +                    count-- | 
|  | 94 | +                } else if (numR <= sums[r] && newSum > sums[r]) { | 
|  | 95 | +                    count++ | 
|  | 96 | +                } | 
|  | 97 | +                modify(segment, nextSegIdx, newSum + sums[r]) | 
|  | 98 | +                lefts[nextSegIdx] = arrIdxL | 
| 95 | 99 |             } | 
| 96 |  | -            merge() | 
| 97 |  | -            return this | 
|  | 100 | +            modify(segment, segIdx, Long.Companion.MAX_VALUE) | 
|  | 101 | +            res++ | 
| 98 | 102 |         } | 
|  | 103 | +        return res | 
| 99 | 104 |     } | 
| 100 | 105 | 
 | 
| 101 |  | -    fun minimumPairRemoval(nums: IntArray): Int { | 
| 102 |  | -        var root = Segment.init(nums) | 
| 103 |  | -        var res = 0 | 
| 104 |  | -        while (!root.ok) { | 
| 105 |  | -            val l = root.li | 
| 106 |  | -            val r = root.ri | 
| 107 |  | -            root.update(l, root.minSum) | 
| 108 |  | -            root = root.remove(r) ?: break | 
| 109 |  | -            res++ | 
|  | 106 | +    private fun modify(segment: LongArray, idx: Int, num: Long) { | 
|  | 107 | +        var idx = idx | 
|  | 108 | +        if (segment[idx] == num) { | 
|  | 109 | +            return | 
|  | 110 | +        } | 
|  | 111 | +        segment[idx] = num | 
|  | 112 | +        while (idx != 0) { | 
|  | 113 | +            idx = (idx - 1) / 2 | 
|  | 114 | +            val l = 2 * idx + 1 | 
|  | 115 | +            val r = 2 * idx + 2 | 
|  | 116 | +            segment[idx] = min(segment[l], segment[r]) | 
| 110 | 117 |         } | 
| 111 |  | -        return res | 
| 112 | 118 |     } | 
| 113 | 119 | } | 
0 commit comments