Editorial for Two (Codeforces 1837F)
Tags: Greedy, binary search

Berland Intercollegiate Contest has just finished. Monocarp and Polycarp, as the jury, are going to conduct an editorial. Unfortunately, the time is limited, since they have to finish before the closing ceremony.There were $$$n$$$ problems in the contest. The problems are numbered from $$$1$$$ to $$$n$$$. The editorial for the $$$i$$$-th problem takes $$$a_i$$$ minutes. Monocarp and Polycarp are going to conduct an editorial for exactly $$$k$$$ of the problems.The editorial goes as follows. They have a full problemset of $$$n$$$ problems before them, in order. They remove $$$n - k$$$ problems without changing the order of the remaining $$$k$$$ problems. Then, Monocarp takes some prefix of these $$$k$$$ problems (possibly, an empty one or all problems). Polycarp takes the remaining suffix of them. After that, they go to different rooms and conduct editorials for their problems in parallel. So, the editorial takes as much time as the longer of these two does.Please, help Monocarp and Polycarp to choose the problems and the split in such a way that the editorial finishes as early as possible. Print the duration of the editorial.

Input: The first line contains a single integer $$$t$$$ ($$$1 \le t \le 10^4$$$) — the number of testcases.The first line of each testcase contains two integers $$$n$$$ and $$$k$$$ ($$$1 \le k \le n \le 3 \cdot 10^5$$$) — the number of problems in the full problemset and the number of problems Monocarp and Polycarp are going to conduct an editorial for.The second line contains $$$n$$$ integers $$$a_1, a_2, \dots, a_n$$$ ($$$1 \le a_i \le 10^9$$$) — the time each editorial takes.The sum of $$$n$$$ over all testcases doesn't exceed $$$3 \cdot 10^5$$$.

Output: For each testcase, print a single integer — the smallest amount of time the editorial takes, if Monocarp and Polycarp can choose which $$$k$$$ of $$$n$$$ problems to conduct an editorial for and how to split them among themselves.


Sample Inputs:
6
5 4
1 10 1 1 1
5 3
1 20 5 15 3
5 3
1 20 3 15 5
10 6
10 8 20 14 3 8 6 4 16 11
10 5
9 9 2 13 15 19 4 9 13 12
1 1
1

Sample Outputs:
2
6
5
21
18
1

Solution (Python):
import os, sys, collections, heapq, itertools
if os.path.exists('in.txt'):
    file = open('in.txt')
    input = file.readline
else:
    input = sys.stdin.readline
from functools import reduce
from itertools import accumulate, chain, combinations, count
from itertools import groupby, permutations, product, zip_longest
from heapq import heapify, heappush, heappop, heapreplace, merge, nlargest, nsmallest
from collections import Counter, defaultdict, deque
from bisect import bisect_left, bisect_right
from math import comb, floor, ceil, inf, gcd
readi = lambda : int(input())
readis = lambda : list(map(int, input().split()))
reads = lambda : input().rstrip()
readss = lambda : input().split()

def solution():
    (n, k) = readis()
    a = readis()
    c = [0] * n

    def check(val):
        use = []
        s = 0
        for (i, x) in enumerate(a):
            if x > val:
                pass
            elif s + x <= val:
                heappush(use, -x)
                s += x
            elif x < -use[0]:
                s += x + heapreplace(use, -x)
            if len(use) >= k:
                return True
            c[i] = len(use)
        use = []
        s = 0
        for i in range(n - 1, 0, -1):
            x = a[i]
            if x > val:
                pass
            elif s + x <= val:
                heappush(use, -x)
                if len(use) + c[i - 1] >= k:
                    return True
                s += x
            elif x < -use[0]:
                s += x + heapreplace(use, -x)
        return 0
    L = list(accumulate(sorted(a[:n // 2])))
    R = list(accumulate(sorted(a[n // 2:])))
    r = sum(sorted(a)[:k])
    l = r // 2
    while l < r:
        mid = (l + r) // 2
        m = bisect_right(L, mid) + bisect_right(R, mid)
        if m >= k:
            res = True
        elif 2 * m < k:
            res = False
        else:
            res = check(mid)
        if res:
            r = mid
        else:
            l = mid + 1
    print(l)
for i in range(readi()):
    solution()

Solution (Python):
import os, sys, collections, heapq, itertools
if os.path.exists('in.txt'):
    file = open('in.txt')
    input = file.readline
else:
    input = sys.stdin.readline
from functools import reduce
from itertools import accumulate, chain, combinations, count
from itertools import groupby, permutations, product, zip_longest
from heapq import heapify, heappush, heappop, heapreplace, merge, nlargest, nsmallest
from collections import Counter, defaultdict, deque
from bisect import bisect_left, bisect_right
from math import comb, floor, ceil, inf, gcd
readi = lambda : int(input())
readis = lambda : list(map(int, input().split()))
reads = lambda : input().rstrip()
readss = lambda : input().split()

def solution():
    (n, k) = readis()
    a = readis()
    c = [0] * n

    def check(val):
        use = []
        s = 0
        for (i, x) in enumerate(a):
            if x > val:
                pass
            elif s + x <= val:
                heappush(use, -x)
                s += x
            elif x < -use[0]:
                s += x + heapreplace(use, -x)
            if len(use) >= k:
                return True
            c[i] = len(use)
        use = []
        s = 0
        for i in range(n - 1, 0, -1):
            x = a[i]
            if x > val:
                pass
            elif s + x <= val:
                heappush(use, -x)
                if len(use) + c[i - 1] >= k:
                    return True
                s += x
            elif x < -use[0]:
                s += x + heapreplace(use, -x)
        return 0
    L = list(accumulate(sorted(a[:n // 2])))
    R = list(accumulate(sorted(a[n // 2:])))
    (l, r) = (0, sum(sorted(a)[:k]))
    while l < r:
        mid = (l + r) // 2
        m = bisect_right(L, mid) + bisect_right(R, mid)
        if m >= k:
            res = True
        elif 2 * m < k:
            res = False
        else:
            res = check(mid)
        if res:
            r = mid
        else:
            l = mid + 1
    print(l)
for i in range(readi()):
    solution()

Solution (Python):
from bisect import bisect_left as lower_bound
from bisect import bisect_right as upper_bound

class FenwickTree:

    def __init__(self, x):
        bit = self.bit = list(x)
        size = self.size = len(bit)
        for i in range(size):
            j = i | i + 1
            if j < size:
                bit[j] += bit[i]

    def update(self, idx, x):
        """"""
        while idx < self.size:
            self.bit[idx] += x
            idx |= idx + 1

    def __call__(self, end):
        """"""
        x = 0
        while end:
            x += self.bit[end - 1]
            end &= end - 1
        return x

    def find_kth(self, k):
        """"""
        idx = -1
        for d in reversed(range(self.size.bit_length())):
            right_idx = idx + (1 << d)
            if right_idx < self.size and self.bit[right_idx] <= k:
                idx = right_idx
                k -= self.bit[idx]
        return (idx + 1, k)

class SortedList:
    block_size = 700

    def __init__(self, iterable=()):
        self.macro = []
        self.micros = [[]]
        self.micro_size = [0]
        self.fenwick = FenwickTree([0])
        self.size = 0
        for item in iterable:
            self.insert(item)

    def insert(self, x):
        i = lower_bound(self.macro, x)
        j = upper_bound(self.micros[i], x)
        self.micros[i].insert(j, x)
        self.size += 1
        self.micro_size[i] += 1
        self.fenwick.update(i, 1)
        if len(self.micros[i]) >= self.block_size:
            self.micros[i:i + 1] = (self.micros[i][:self.block_size >> 1], self.micros[i][self.block_size >> 1:])
            self.micro_size[i:i + 1] = (self.block_size >> 1, self.block_size >> 1)
            self.fenwick = FenwickTree(self.micro_size)
            self.macro.insert(i, self.micros[i + 1][0])

    def pop(self, k=-1):
        (i, j) = self._find_kth(k)
        self.size -= 1
        self.micro_size[i] -= 1
        self.fenwick.update(i, -1)
        return self.micros[i].pop(j)

    def __getitem__(self, k):
        (i, j) = self._find_kth(k)
        return self.micros[i][j]

    def count(self, x):
        return self.upper_bound(x) - self.lower_bound(x)

    def __contains__(self, x):
        return self.count(x) > 0

    def lower_bound(self, x):
        i = lower_bound(self.macro, x)
        return self.fenwick(i) + lower_bound(self.micros[i], x)

    def upper_bound(self, x):
        i = upper_bound(self.macro, x)
        return self.fenwick(i) + upper_bound(self.micros[i], x)

    def _find_kth(self, k):
        return self.fenwick.find_kth(k + self.size if k < 0 else k)

    def __len__(self):
        return self.size

    def __iter__(self):
        return (x for micro in self.micros for x in micro)

    def __repr__(self):
        return str(list(self))
enum = enumerate
test = int(input())
for testtest in range(test):
    (n, k) = map(int, input().split())
    L = list(map(int, input().split()))
    A = SortedList()
    B = SortedList(L)
    a = 0
    b = k
    sa = 0
    sb = 0
    ans = 0
    for i in range(k):
        ans += B[i]
    sb = ans
    for v in L:
        if B[b - 1] >= v and b != 0:
            b -= 1
            sb -= v
        B.pop(B.lower_bound(v))
        val = 0
        if a > 0:
            val = A[a - 1]
        if v < val:
            sa += v
            sa -= val
        A.insert(v)
        if a + b < k:
            sa += A[a]
            a += 1
        while sa > sb and b < len(B) and (sb + B[b] < sa):
            sb += B[b]
            sa -= A[a - 1]
            a -= 1
            b += 1
        while sb > sa and a < len(A) and (sa + A[a] < sb):
            sa += A[a]
            sb -= B[b - 1]
            b -= 1
            a += 1
        ans = min(ans, max(sa, sb))
    print(ans)