Doremy's Number Line (Codeforces 1764E)
Tags: Greedy, sortings

Doremy has two arrays $$$a$$$ and $$$b$$$ of $$$n$$$ integers each, and an integer $$$k$$$.Initially, she has a number line where no integers are colored. She chooses a permutation $$$p$$$ of $$$[1,2,\ldots,n]$$$ then performs $$$n$$$ moves. On the $$$i$$$-th move she does the following:  Pick an uncolored integer $$$x$$$ on the number line such that either:   $$$x \leq a_{p_i}$$$; or  there exists a colored integer $$$y$$$ such that $$$y \leq a_{p_i}$$$ and $$$x \leq y+b_{p_i}$$$.   Color integer $$$x$$$ with color $$$p_i$$$. Determine if the integer $$$k$$$ can be colored with color $$$1$$$.

Input: The input consists of multiple test cases. The first line contains a single integer $$$t$$$ ($$$1\le t\le 10^4$$$) — the number of test cases. The description of the test cases follows.The first line contains two integers $$$n$$$ and $$$k$$$ ($$$1 \le n \le 10^5$$$, $$$1 \le k \le 10^9$$$).Each of the following $$$n$$$ lines contains two integers $$$a_i$$$ and $$$b_i$$$ ($$$1 \le a_i,b_i \le 10^9$$$).It is guaranteed that the sum of $$$n$$$ over all test cases does not exceed $$$10^5$$$.

Output: For each test case, output "YES" (without quotes) if the point $$$k$$$ can be colored with color $$$1$$$. Otherwise, output "NO" (without quotes).You can output "YES" and "NO" in any case (for example, strings "yEs", "yes" and "Yes" will be recognized as a positive response).

Note: For the first test case, it is impossible to color point $$$16$$$ with color $$$1$$$.For the second test case, $$$p=[2,1,3,4]$$$ is one possible choice, the detail is shown below.  On the first move, pick $$$x=8$$$ and color it with color $$$2$$$ since $$$x=8$$$ is uncolored and $$$x \le a_2$$$.  On the second move, pick $$$x=16$$$ and color it with color $$$1$$$ since there exists a colored point $$$y=8$$$ such that $$$y\le a_1$$$ and $$$x \le y + b_1$$$.  On the third move, pick $$$x=0$$$ and color it with color $$$3$$$ since $$$x=0$$$ is uncolored and $$$x \le a_3$$$.  On the forth move, pick $$$x=-2$$$ and color it with color $$$4$$$ since $$$x=-2$$$ is uncolored and $$$x \le a_4$$$.  In the end, point $$$-2,0,8,16$$$ are colored with color $$$4,3,2,1$$$, respectively. For the third test case, $$$p=[2,1,4,3]$$$ is one possible choice.For the fourth test case, $$$p=[2,3,4,1]$$$ is one possible choice.

Sample Inputs:
6
4 16
5 3
8 12
10 7
15 1
4 16
8 12
10 7
15 1
5 3
4 16
10 7
15 1
5 3
8 12
4 16
15 1
5 3
8 12
10 7
1 1000000000
500000000 500000000
2 1000000000
1 999999999
1 1

Sample Outputs:
NO
YES
YES
YES
NO
YES

Solution (Python):
import io, os
input = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline
t = int(input())
for tidx in range(t):
    (n, k) = [int(x) for x in input().split()]
    n = n - 1
    c = [(0, 0)] * n
    d = [0] * n
    (a1, b1) = [int(x) for x in input().split()]
    if n > 0:
        for i in range(n):
            (ai, bi) = [int(x) for x in input().split()]
            c[i] = (ai + bi, ai, bi)
        c.sort()
        d[0] = c[0][1]
        for i in range(1, n):
            d[i] = max(c[i][1], min(c[i][1], d[i - 1]) + c[i][2], c[i - 1][0])
        if max(a1, min(a1, d[n - 1]) + b1) >= k:
            print('YES')
        else:
            print('NO')
    elif a1 >= k:
        print('YES')
    else:
        print('NO')

Solution (Python):
def divisors(M):
    d = []
    i = 1
    while M >= i ** 2:
        if M % i == 0:
            d.append(i)
            if i ** 2 != M:
                d.append(M // i)
        i = i + 1
    return d

def popcount(x):
    x = x - (x >> 1 & 1431655765)
    x = (x & 858993459) + (x >> 2 & 858993459)
    x = x + (x >> 4) & 252645135
    x = x + (x >> 8)
    x = x + (x >> 16)
    return x & 127

def eratosthenes(n):
    res = [0 for i in range(n + 1)]
    prime = set([])
    for i in range(2, n + 1):
        if not res[i]:
            prime.add(i)
            for j in range(1, n // i + 1):
                res[i * j] = 1
    return prime

def factorization(n):
    res = []
    for p in prime:
        if n % p == 0:
            while n % p == 0:
                n //= p
            res.append(p)
    if n != 1:
        res.append(n)
    return res

def euler_phi(n):
    res = n
    for x in range(2, n + 1):
        if x ** 2 > n:
            break
        if n % x == 0:
            res = res // x * (x - 1)
            while n % x == 0:
                n //= x
    if n != 1:
        res = res // n * (n - 1)
    return res

def ind(b, n):
    res = 0
    while n % b == 0:
        res += 1
        n //= b
    return res

def isPrimeMR(n):
    d = n - 1
    d = d // (d & -d)
    L = [2, 3, 5, 7, 11, 13, 17]
    for a in L:
        t = d
        y = pow(a, t, n)
        if y == 1:
            continue
        while y != n - 1:
            y = y * y % n
            if y == 1 or t == n - 1:
                return 0
            t <<= 1
    return 1

def findFactorRho(n):
    from math import gcd
    m = 1 << n.bit_length() // 8
    for c in range(1, 99):
        f = lambda x: (x * x + c) % n
        (y, r, q, g) = (2, 1, 1, 1)
        while g == 1:
            x = y
            for i in range(r):
                y = f(y)
            k = 0
            while k < r and g == 1:
                ys = y
                for i in range(min(m, r - k)):
                    y = f(y)
                    q = q * abs(x - y) % n
                g = gcd(q, n)
                k += m
            r <<= 1
        if g == n:
            g = 1
            while g == 1:
                ys = f(ys)
                g = gcd(abs(x - ys), n)
        if g < n:
            if isPrimeMR(g):
                return g
            elif isPrimeMR(n // g):
                return n // g
            return findFactorRho(g)

def primeFactor(n):
    i = 2
    ret = {}
    rhoFlg = 0
    while i * i <= n:
        k = 0
        while n % i == 0:
            n //= i
            k += 1
        if k:
            ret[i] = k
        i += 1 + i % 2
        if i == 101 and n >= 2 ** 20:
            while n > 1:
                if isPrimeMR(n):
                    (ret[n], n) = (1, 1)
                else:
                    rhoFlg = 1
                    j = findFactorRho(n)
                    k = 0
                    while n % j == 0:
                        n //= j
                        k += 1
                    ret[j] = k
    if n > 1:
        ret[n] = 1
    if rhoFlg:
        ret = {x: ret[x] for x in sorted(ret)}
    return ret

def divisors(n):
    res = [1]
    prime = primeFactor(n)
    for p in prime:
        newres = []
        for d in res:
            for j in range(prime[p] + 1):
                newres.append(d * p ** j)
        res = newres
    res.sort()
    return res

def xorfactorial(num):
    if num == 0:
        return 0
    elif num == 1:
        return 1
    elif num == 2:
        return 3
    elif num == 3:
        return 0
    else:
        x = baseorder(num)
        return 2 ** x * ((num - 2 ** x + 1) % 2) + function(num - 2 ** x)

def xorconv(n, X, Y):
    if n == 0:
        res = [X[0] * Y[0] % mod]
        return res
    x = [X[i] + X[i + 2 ** (n - 1)] for i in range(2 ** (n - 1))]
    y = [Y[i] + Y[i + 2 ** (n - 1)] for i in range(2 ** (n - 1))]
    z = [X[i] - X[i + 2 ** (n - 1)] for i in range(2 ** (n - 1))]
    w = [Y[i] - Y[i + 2 ** (n - 1)] for i in range(2 ** (n - 1))]
    res1 = xorconv(n - 1, x, y)
    res2 = xorconv(n - 1, z, w)
    former = [(res1[i] + res2[i]) * inv for i in range(2 ** (n - 1))]
    latter = [(res1[i] - res2[i]) * inv for i in range(2 ** (n - 1))]
    former = list(map(lambda x: x % mod, former))
    latter = list(map(lambda x: x % mod, latter))
    return former + latter

def merge_sort(A, B):
    (pos_A, pos_B) = (0, 0)
    (n, m) = (len(A), len(B))
    res = []
    while pos_A < n and pos_B < m:
        (a, b) = (A[pos_A], B[pos_B])
        if a < b:
            res.append(a)
            pos_A += 1
        else:
            res.append(b)
            pos_B += 1
    res += A[pos_A:]
    res += B[pos_B:]
    return res

class UnionFindVerSize:

    def __init__(self, N):
        self._parent = [n for n in range(0, N)]
        self._size = [1] * N
        self.group = N

    def find_root(self, x):
        if self._parent[x] == x:
            return x
        self._parent[x] = self.find_root(self._parent[x])
        stack = [x]
        while self._parent[stack[-1]] != stack[-1]:
            stack.append(self._parent[stack[-1]])
        for v in stack:
            self._parent[v] = stack[-1]
        return self._parent[x]

    def unite(self, x, y):
        gx = self.find_root(x)
        gy = self.find_root(y)
        if gx == gy:
            return
        self.group -= 1
        if self._size[gx] < self._size[gy]:
            self._parent[gx] = gy
            self._size[gy] += self._size[gx]
        else:
            self._parent[gy] = gx
            self._size[gx] += self._size[gy]

    def get_size(self, x):
        return self._size[self.find_root(x)]

    def is_same_group(self, x, y):
        return self.find_root(x) == self.find_root(y)

class WeightedUnionFind:

    def __init__(self, N):
        self.parent = [i for i in range(N)]
        self.size = [1 for i in range(N)]
        self.val = [0 for i in range(N)]
        self.flag = True
        self.edge = [[] for i in range(N)]

    def dfs(self, v, pv):
        stack = [(v, pv)]
        new_parent = self.parent[pv]
        while stack:
            (v, pv) = stack.pop()
            self.parent[v] = new_parent
            for (nv, w) in self.edge[v]:
                if nv != pv:
                    self.val[nv] = self.val[v] + w
                    stack.append((nv, v))

    def unite(self, x, y, w):
        if not self.flag:
            return
        if self.parent[x] == self.parent[y]:
            self.flag = self.val[x] - self.val[y] == w
            return
        if self.size[self.parent[x]] > self.size[self.parent[y]]:
            self.edge[x].append((y, -w))
            self.edge[y].append((x, w))
            self.size[x] += self.size[y]
            self.val[y] = self.val[x] - w
            self.dfs(y, x)
        else:
            self.edge[x].append((y, -w))
            self.edge[y].append((x, w))
            self.size[y] += self.size[x]
            self.val[x] = self.val[y] + w
            self.dfs(x, y)

class Dijkstra:

    class Edge:

        def __init__(self, _to, _cost):
            self.to = _to
            self.cost = _cost

    def __init__(self, V):
        self.G = [[] for i in range(V)]
        self._E = 0
        self._V = V

    @property
    def E(self):
        return self._E

    @property
    def V(self):
        return self._V

    def add_edge(self, _from, _to, _cost):
        self.G[_from].append(self.Edge(_to, _cost))
        self._E += 1

    def shortest_path(self, s):
        import heapq
        que = []
        d = [10 ** 15] * self.V
        d[s] = 0
        heapq.heappush(que, (0, s))
        while len(que) != 0:
            (cost, v) = heapq.heappop(que)
            if d[v] < cost:
                continue
            for i in range(len(self.G[v])):
                e = self.G[v][i]
                if d[e.to] > d[v] + e.cost:
                    d[e.to] = d[v] + e.cost
                    heapq.heappush(que, (d[e.to], e.to))
        return d

def Z_algorithm(s):
    N = len(s)
    Z_alg = [0] * N
    Z_alg[0] = N
    i = 1
    j = 0
    while i < N:
        while i + j < N and s[j] == s[i + j]:
            j += 1
        Z_alg[i] = j
        if j == 0:
            i += 1
            continue
        k = 1
        while i + k < N and k + Z_alg[k] < j:
            Z_alg[i + k] = Z_alg[k]
            k += 1
        i += k
        j -= k
    return Z_alg

class BIT:

    def __init__(self, n, mod=0):
        self.BIT = [0] * (n + 1)
        self.num = n
        self.mod = mod

    def query(self, idx):
        res_sum = 0
        mod = self.mod
        while idx > 0:
            res_sum += self.BIT[idx]
            if mod:
                res_sum %= mod
            idx -= idx & -idx
        return res_sum

    def update(self, idx, x):
        mod = self.mod
        while idx <= self.num:
            self.BIT[idx] += x
            if mod:
                self.BIT[idx] %= mod
            idx += idx & -idx
        return

class dancinglink:

    def __init__(self, n, debug=False):
        self.n = n
        self.debug = debug
        self._left = [i - 1 for i in range(n)]
        self._right = [i + 1 for i in range(n)]
        self.exist = [True for i in range(n)]

    def pop(self, k):
        if self.debug:
            assert self.exist[k]
        L = self._left[k]
        R = self._right[k]
        if L != -1:
            if R != self.n:
                (self._right[L], self._left[R]) = (R, L)
            else:
                self._right[L] = self.n
        elif R != self.n:
            self._left[R] = -1
        self.exist[k] = False

    def left(self, idx, k=1):
        if self.debug:
            assert self.exist[idx]
        res = idx
        while k:
            res = self._left[res]
            if res == -1:
                break
            k -= 1
        return res

    def right(self, idx, k=1):
        if self.debug:
            assert self.exist[idx]
        res = idx
        while k:
            res = self._right[res]
            if res == self.n:
                break
            k -= 1
        return res

class SparseTable:

    def __init__(self, A, merge_func, ide_ele):
        N = len(A)
        self.merge_func = merge_func
        self.lg = [0] * (N + 1)
        for i in range(2, N + 1):
            self.lg[i] = self.lg[i >> 1] + 1
        self.pow_2 = [pow(2, i) for i in range(20)]
        self.table = [None] * (self.lg[N] + 1)
        st0 = self.table[0] = [a for a in A]
        b = 1
        for i in range(self.lg[N]):
            st0 = self.table[i + 1] = [self.merge_func(u, v) for (u, v) in zip(st0, st0[b:])]
            b <<= 1

    def query(self, s, t):
        b = t - s + 1
        m = self.lg[b]
        return self.merge_func(self.table[m][s], self.table[m][t - self.pow_2[m] + 1])

class BinaryTrie:

    class node:

        def __init__(self, val):
            self.left = None
            self.right = None
            self.max = val

    def __init__(self):
        self.root = self.node(-10 ** 15)

    def append(self, key, val):
        pos = self.root
        for i in range(29, -1, -1):
            pos.max = max(pos.max, val)
            if key >> i & 1:
                if pos.right is None:
                    pos.right = self.node(val)
                    pos = pos.right
                else:
                    pos = pos.right
            elif pos.left is None:
                pos.left = self.node(val)
                pos = pos.left
            else:
                pos = pos.left
        pos.max = max(pos.max, val)

    def search(self, M, xor):
        res = -10 ** 15
        pos = self.root
        for i in range(29, -1, -1):
            if pos is None:
                break
            if M >> i & 1:
                if xor >> i & 1:
                    if pos.right:
                        res = max(res, pos.right.max)
                    pos = pos.left
                else:
                    if pos.left:
                        res = max(res, pos.left.max)
                    pos = pos.right
            elif xor >> i & 1:
                pos = pos.right
            else:
                pos = pos.left
        if pos:
            res = max(res, pos.max)
        return res

def solveequation(edge, ans, n, m):

    def dfs(v):
        used[v] = True
        r = ans[v]
        for (to, dire, id) in edge[v]:
            if used[to]:
                continue
            y = dfs(to)
            if dire == -1:
                x[id] = y
            else:
                x[id] = -y
            r += y
        return r
    x = [0] * m
    used = [False] * n
    for v in range(n):
        if used[v]:
            continue
        y = dfs(v)
        if y != 0:
            return False
    return x

class slope_trick:

    def __init__(self):
        self.L = [10 ** 17]
        self.R = [10 ** 17]
        self.min_f = 0
        self.x_left = 0
        self.x_right = 0

    def add_right(self, a):
        a -= self.x_left
        l0 = -self.L[0]
        self.min_f = self.min_f + max(0, l0 - a)
        if l0 <= a:
            a += self.x_left
            a -= self.x_right
            heappush(self.R, a)
        else:
            heappush(self.L, -a)
            a = -heappop(self.L)
            a += self.x_left
            a -= self.x_right
            heappush(self.R, a)

    def add_left(self, a):
        a -= self.x_right
        r0 = self.R[0]
        self.min_f = self.min_f + max(0, a - r0)
        if a <= r0:
            a += self.x_right
            a -= self.x_left
            heappush(self.L, -a)
        else:
            heappush(self.R, a)
            a = heappop(self.R)
            a += self.x_right
            a -= self.x_left
            heappush(self.L, -a)

    def add_abs(self, a):
        self.add_left(a)
        self.add_right(a)

    def change_min_slide(self, a, b):
        self.x_left += a
        self.x_right += b

    def get_val(self, x):
        L = [-l + self.x_left for l in self.L]
        L.sort()
        R = [r + self.x_right for r in self.R]
        R.sort()
        res = self.min_f
        if 0 < L[-1]:
            L = L[::-1]
            n = len(L)
            for i in range(n):
                c0 = L[i]
                c1 = L[i + 1]
                if c1 <= x <= c0:
                    res += (i + 1) * (c0 - x)
                    break
                else:
                    res += (i + 1) * (c0 - c1)
            return res
        elif L[-1] <= x <= R[0]:
            return res
        else:
            n = len(R)
            for i in range(n):
                c0 = R[i]
                c1 = R[i + 1]
                if c0 <= x <= c1:
                    res += (i + 1) * (x - c0)
                    break
                else:
                    res += (i + 1) * (c1 - c0)
            return res

class SegmentTree:

    def __init__(self, init_val, segfunc, ide_ele):
        n = len(init_val)
        self.segfunc = segfunc
        self.ide_ele = ide_ele
        self.num = 1 << (n - 1).bit_length()
        self.tree = [ide_ele] * 2 * self.num
        self.size = n
        for i in range(n):
            self.tree[self.num + i] = init_val[i]
        for i in range(self.num - 1, 0, -1):
            self.tree[i] = self.segfunc(self.tree[2 * i], self.tree[2 * i + 1])

    def update(self, k, x):
        k += self.num
        self.tree[k] = x
        while k > 1:
            k >>= 1
            self.tree[k] = self.segfunc(self.tree[2 * k], self.tree[2 * k + 1])

    def query(self, l, r):
        if r == self.size:
            r = self.num
        res = self.ide_ele
        l += self.num
        r += self.num
        right = []
        while l < r:
            if l & 1:
                res = self.segfunc(res, self.tree[l])
                l += 1
            if r & 1:
                right.append(self.tree[r - 1])
            l >>= 1
            r >>= 1
        for e in right[::-1]:
            res = self.segfunc(res, e)
        return res

    def bisect_l(self, l, r, x):
        l += self.num
        r += self.num
        Lmin = -1
        Rmin = -1
        while l < r:
            if l & 1:
                if self.tree[l] <= x and Lmin == -1:
                    Lmin = l
                l += 1
            if r & 1:
                if self.tree[r - 1] <= x:
                    Rmin = r - 1
            l >>= 1
            r >>= 1
        if Lmin != -1:
            pos = Lmin
            while pos < self.num:
                if self.tree[2 * pos] <= x:
                    pos = 2 * pos
                else:
                    pos = 2 * pos + 1
            return pos - self.num
        elif Rmin != -1:
            pos = Rmin
            while pos < self.num:
                if self.tree[2 * pos] <= x:
                    pos = 2 * pos
                else:
                    pos = 2 * pos + 1
            return pos - self.num
        else:
            return -1
import sys, random, bisect
from collections import deque, defaultdict
from heapq import heapify, heappop, heappush
from itertools import permutations
from math import gcd, log
input = lambda : sys.stdin.buffer.readline()
mi = lambda : map(int, input().split())
li = lambda : list(mi())
p = 2 ** 30 - 1

def solve(n, k, ab):
    (a1, b1) = ab[0]
    card = []
    for (a, b) in ab[1:]:
        card.append((a + b << 30) + p - a)
    card.sort()
    if n == 1:
        return k <= a1
    card.sort()
    a = p - (card[0] & p)
    b = (card[0] >> 30) - a
    M = a
    a_max = a
    for i in range(1, n - 1):
        ai = p - (card[i] & p)
        bi = (card[i] >> 30) - ai
        if ai <= a_max:
            M = ai + bi
        elif ai <= M:
            M = ai + bi
        elif a + b < ai:
            M = max(M + bi, ai)
        else:
            M = max(M + bi, a + b)
        a_max = max(a_max, ai)
        (a, b) = (ai, bi)
    M = max(a1, min(a1, M) + b1)
    return k <= M

def brute(n, k, card):
    L = permutations([i for i in range(n)])
    for p in L:
        M = -10 ** 10
        for i in range(n):
            if p[i] != 0:
                (a, b) = card[p[i]]
                M = max(M, max(a, min(a, M) + b))
            else:
                (a, b) = card[p[i]]
                if k <= max(a, min(a, M) + b):
                    return True
                break
    return False
while False:
    n = random.randint(1, 5)
    k = random.randint(1, 100)
    card = [(random.randint(1, 40), random.randint(1, 40)) for i in range(n)]
    if solve(n, k, card) != brute(n, k, card):
        print(n, k, card)
        print(solve(n, k, card), brute(n, k, card))
        assert False
for _ in range(int(input())):
    (n, k) = mi()
    ab = [tuple(mi()) for i in range(n)]
    print('YES' if solve(n, k, ab) else 'NO')

Solution (Python):
import sys
input = sys.stdin.readline
from operator import itemgetter
from heapq import heappop, heappush
t = int(input())
for tests in range(t):
    (n, k) = map(int, input().split())
    (XA, XB) = map(int, input().split())
    A = [list(map(int, input().split())) for i in range(n - 1)]
    if XA >= k:
        print('YES')
        continue
    if XA + XB < k:
        print('NO')
        continue
    H = []
    A.sort(key=lambda x: x[0] + x[1], reverse=True)
    now = k - XB
    ind = 0
    while True:
        while ind < len(A) and A[ind][0] + A[ind][1] >= now:
            heappush(H, (A[ind][0], A[ind][1]))
            ind += 1
        if H:
            (a, b) = heappop(H)
            if a >= now:
                print('YES')
                break
            elif a + b >= now:
                now = now - b
        else:
            print('NO')
            break