Messenger in MAC (Codeforces 1935C)
Tags: Greedy, binary search

In the new messenger for the students of the Master's Assistance Center, Keftemerum, an update is planned, in which developers want to optimize the set of messages shown to the user. There are a total of $$$n$$$ messages. Each message is characterized by two integers $$$a_i$$$ and $$$b_i$$$. The time spent reading the set of messages with numbers $$$p_1, p_2, \ldots, p_k$$$ ($$$1 \le p_i \le n$$$, all $$$p_i$$$ are distinct) is calculated by the formula:$$$$$$\Large \sum_{i=1}^{k} a_{p_i} + \sum_{i=1}^{k - 1} |b_{p_i} - b_{p_{i+1}}|$$$$$$Note that the time to read a set of messages consisting of one message with number $$$p_1$$$ is equal to $$$a_{p_1}$$$. Also, the time to read an empty set of messages is considered to be $$$0$$$.The user can determine the time $$$l$$$ that he is willing to spend in the messenger. The messenger must inform the user of the maximum possible size of the set of messages, the reading time of which does not exceed $$$l$$$. Note that the maximum size of the set of messages can be equal to $$$0$$$.The developers of the popular messenger failed to implement this function, so they asked you to solve this problem.

Input: Each test consists of multiple test cases. The first line contains a single integer $$$t$$$ ($$$1 \leq t \leq 5 \cdot 10^4$$$) — the number of test cases. The description of the test cases follows.The first line of each test case contains two integers $$$n$$$ and $$$l$$$ ($$$1 \leq n \leq 2000$$$, $$$1 \leq l \leq 10^9$$$) — the number of messages and the time the user is willing to spend in the messenger.The $$$i$$$-th of the next $$$n$$$ lines contains two integers $$$a_i$$$ and $$$b_i$$$ ($$$1 \le a_i, b_i \le 10^9$$$) — characteristics of the $$$i$$$-th message.It is guaranteed that the sum of $$$n^2$$$ over all test cases does not exceed $$$4 \cdot 10^6$$$.

Output: For each test case, output a single integer — the maximum possible size of a set of messages, the reading time of which does not exceed $$$l$$$.

Note: In the first test case, you can take a set of three messages with numbers $$$p_1 = 3$$$, $$$p_2 = 2$$$, and $$$p_3 = 5$$$. The time spent reading this set is equal to $$$a_3 + a_2 + a_5 + |b_3 - b_2| + |b_2 - b_5| = 2 + 1 + 2 + |4 - 5| + |5 - 3| = 8$$$.In the second test case, you can take a set of one message with number $$$p_1 = 1$$$. The time spent reading this set is equal to $$$a_1 = 4$$$.In the fifth test case, it can be shown that there is no such non-empty set of messages, the reading time of which does not exceed $$$l$$$.

Sample Inputs:
5
5 8
4 3
1 5
2 4
4 3
2 3
1 6
4 10
3 12
4 8
2 1
2 12
5 26
24 7
8 28
30 22
3 8
17 17
5 14
15 3
1000000000 998244353
179 239
228 1337
993 1007

Sample Outputs:
3
1
2
1
0

Solution (Python):
import bisect
import os
import sys
import time
from sys import stdin, stdout
import math
import queue
from bisect import bisect_left
import random
import heapq
from heapq import heappop, heappush
from math import *
sys.setrecursionlimit(10000)
PRIME = 72057594037927931
DOMAIN = 26

def solveBegin(index, lst, l):
    begin = lst[index][1]
    if lst[index][0] > l:
        return 0
    currentA = lst[index][0]
    currentGlobal = lst[index][0]
    (a, b) = lst[index]
    tas = []
    v = 1
    v = max(v, len(tas))
    for i in range(index + 1, len(lst)):
        (a, b) = lst[i]
        tscore = currentA + a + b - begin
        if tscore <= l:
            heapq.heappush(tas, (-a, b, index))
            currentA += a
            v += 1
        elif len(tas) > 0:
            (p, tmp, idx) = tas[0]
            scoretmp = currentA + a + b - begin + p
            if -p > a:
                heapq.heappop(tas)
                heapq.heappush(tas, (-a, b, index))
                currentA = currentA + a + p
                currentGlobal = currentA + b - begin
    return v

def solve(t, lst, l):
    lstSort = []
    for u in lst:
        lstSort.append((u[1], u))
    lstSort.sort()
    lst = []
    for (u, tu) in lstSort:
        lst.append(tu)
    if t == 0:
        aa = 1
    m = 0
    for i in range(len(lst)):
        mtmp = solveBegin(i, lst, l)
        m = max(m, mtmp)
    return str(m)

def solveNaif(t, lst, l):
    if t == 1:
        aa = 1
    v = 0
    fmt = '{0:0' + str(len(lst)) + 'b}'
    for i in range(2 ** len(lst)):
        choose = list(fmt.format(i))
        current = 0
        minS = float('Inf')
        maxS = 0
        nbr = 0
        for i in range(len(lst)):
            if choose[i] == '1':
                nbr += 1
                (a, b) = lst[i]
                current += a
                minS = min(minS, b)
                maxS = max(maxS, b)
        if current + maxS - minS <= l:
            if v < nbr:
                v = max(v, nbr)
                print(choose)
    return str(v)

def solveInput(inputStream):
    t = int(inputStream.readline().strip())
    reponses = []
    for i in range(t):
        (n, l) = tuple(map(int, inputStream.readline().strip().split(' ')))
        lst = []
        for k in range(n):
            lst.append(tuple(map(int, inputStream.readline().strip().split(' '))))
        res = solve(i, lst, l)
        reponses.append(res)
    return '\n'.join(reponses)

def test():
    inner_start_time = time.time()
    lst = [(925, 1187), (145, 451), (353, 4161), (562, 3988)]
    l = 4796
    r = solve(0, lst, l)
    rn = solveNaif(0, lst, l)
    if rn != r:
        print('Error')
        print(lst)
        print('l=' + str(l) + ' rn=' + rn + ' r=' + r)
        exit(1)
    for k in range(100000):
        lst = []
        for i in range(4):
            lst.append((random.randrange(1, 1000), random.randrange(1, 10000)))
        l = random.randrange(1, 20 * 1000)
        r = solve(0, lst, l)
        rn = solveNaif(0, lst, l)
        if rn != r:
            print('Error')
            print(lst)
            print('l=' + str(l))
            exit(1)
    inner_time = time.time() - inner_start_time
    print(inner_time, 'seconde')

def diff(reponse, attendu):
    linesRep = reponse.splitlines()
    linesAtt = attendu.splitlines()
    minLen = min(len(linesRep), len(linesAtt))
    print('Reponse vs Attendu')
    i = 0
    ret = True
    while i < minLen:
        res = linesRep[i].strip()
        att = linesAtt[i].strip()
        if not res == att:
            print('=> [' + res + '] [' + att + ']')
            ret = False
        else:
            print('   [' + res + '] [' + att + ']')
        i = i + 1
    return ret

def diff_float(reponse, attendu, fPrint):
    linesRep = reponse.splitlines()
    linesAtt = attendu.splitlines()
    minLen = min(len(linesRep), len(linesAtt))
    print('Reponse vs Attendu')
    i = 0
    different = False
    while i < minLen:
        res = linesRep[i].strip()
        att = linesAtt[i].strip()
        d = math.fabs(float(res) - float(att))
        if not d < 1e-06:
            different = True
            if fPrint:
                print('=> [' + res + '] [' + att + ']')
            else:
                return True
        elif fPrint:
            print('   [' + res + '] [' + att + ']')
        i = i + 1
    return different
if len(sys.argv) >= 2 and sys.argv[1] == 'SAMPLE':
    sampleFolder = 'D:\\BCR\\projets\\swerc\\intellij3_python\\data\\'
    files = os.listdir(sampleFolder)
    reussi = 0
    nbCasDeTest = 0
    for file in files:
        if '.in' in file:
            outputFile = sampleFolder + file.replace('.in', '.ans')
            if not os.path.exists(outputFile):
                continue
            nbCasDeTest += 1
            print('---------------------------')
            print('Cas de test ' + file)
            attendu = open(outputFile, 'r').read()
            inputStream = open(sampleFolder + file, 'r')
            inner_start_time = time.time()
            res = solveInput(inputStream)
            inner_time = time.time() - inner_start_time
            check = diff(res, attendu)
            if check:
                reussi = reussi + 1
            else:
                print("Erreur avec l'input : ", sampleFolder + file, inner_time, 'seconde')
                print('Obtenu  : [' + res + ']\nAttendu : [' + attendu + ']')
    print('')
    print('--------------BILAN--------------')
    print(str(reussi) + '/' + str(nbCasDeTest))
elif len(sys.argv) >= 2 and sys.argv[1] == 'TEST':
    print('--------------TEST--------------')
    test()
else:
    ret = solveInput(stdin)
    stdout.write(ret)

Solution (Python):
import sys
input = sys.stdin.readline
import heapq

def readList():
    return list(map(int, input().split()))

def readInt():
    return int(input())

def readInts():
    return map(int, input().split())

def readStr():
    return input().strip()

def solve():
    (n, l) = readInts()
    me = [readList() for _ in range(n)]
    idx = sorted([i for i in range(n)], key=lambda x: me[x][1])
    ans = 0
    for i in range(n):
        t = me[idx[i]][0]
        qu = [-t]
        if t > l:
            continue
        ans = max(ans, 1)
        for j in range(i + 1, n):
            v = t + me[idx[j]][0]
            h = me[idx[j]][1] - me[idx[i]][1]
            if v + h <= l:
                t = v
                heapq.heappush(qu, -me[idx[j]][0])
            elif qu and -qu[0] > me[idx[j]][0]:
                t = v + qu[0]
                heapq.heappushpop(qu, -me[idx[j]][0])
            if v + h <= l:
                ans = max(ans, len(qu))
    return ans
for _ in range(int(input())):
    print(solve())

Solution (Python):
import math
import heapq
import itertools
import bisect
import random
import time
from collections import deque
import sys
from cmath import exp, pi
from functools import cmp_to_key
input = sys.stdin.readline
t = int(input())
for _ in range(t):
    (n, l) = map(int, input().split())
    arr = []
    for i in range(n):
        (a, b) = map(int, input().split())
        arr.append([a, b])
    arr = sorted(arr, key=lambda x: x[1])
    ans = 0
    for i in range(n):
        queue = []
        s = 0
        for j in range(i, n):
            d = arr[j][1] - arr[i][1]
            s += arr[j][0]
            heapq.heappush(queue, -arr[j][0])
            if d + s > l:
                s += heapq.heappop(queue)
            ans = max(ans, len(queue))
    print(ans)