{ }codelper
binary searchgreedy64-bit integersP0213

Stretching every streetlight equally

A straight road runs from 0 to L, and there are n streetlights beside it. Streetlight i stands at position pᵢ and currently lights from pᵢ − rᵢ to pᵢ + rᵢ (both ends included). Every streetlight can be fitted with a booster of the same strength t. t is an integer, 0 or more, and with the booster every streetlight lights from pᵢ − rᵢ − t to pᵢ + rᵢ + t. At most k streetlights can be switched on. The road is fully lit if the union of the ranges of the switched-on lights contains every point from 0 to L (non-integer points included). If two ranges meet at a single end point, there is no gap between them. Print the smallest t with which the road can be fully lit.

Input
The first line has n, L and k, and each of the next n lines has pᵢ and rᵢ, separated by a space.
1 ≤ n ≤ 10,000, 0 ≤ L ≤ 1,000,000,000, 1 ≤ k ≤ n, 0 ≤ pᵢ ≤ L, 0 ≤ rᵢ ≤ 1,000,000,000
Output
One number, the smallest t
Example 1 · stdin
4 10 2
1 1
4 2
9 0
7 1
stdout
2
main.py
Input (stdin)

When you run, these lines are fed to input()/stdin in order (the program doesn't stop to ask).

$ Ready

Open this on a computer to write and run code. On mobile you can read the problem and the solutions.

In other languagesShow solutions by language · 8 languages

Raising t only widens every range, so if t works, t + 1 works too.
That is why you binary search the answer t: start with lo = 0 and hi = L (with t = L any single light on the road covers [0, L]); if mid works set hi = mid, otherwise lo = mid + 1, and lo is the answer.
Whether one t works is a greedy check: among the lights that can light need, the leftmost point not yet lit (0 at first), switch on the one whose right end reaches farthest, move need to that end, and repeat up to k times.
All ranges grow by the same t, so the order of the left ends p − r does not depend on t — sort once at the start, and each check is one scan from the front (n log n + 31 × n).
Trap one: the ranges are closed, so ranges that just touch are connected (left end − t <= need).
Writing < treats [−1, 5] and [5, 11] as disconnected and makes the answer too large.
Trap two: p + r is at most 2 billion, which barely fits a 32-bit int (about 2.1 billion), but adding t during the search takes it to 3 billion and it overflows.
C and C++ need long long, Java and C# long; Python integers have no limit, and JavaScript, TypeScript and Craykon numbers are doubles, so they work as they are.
Trap three: sorting the lights by position p differs from the order of the left ends when the radii differ, so the scan from the front misses a light whose left end is smaller.
Trap four: throwing away a working mid with hi = mid - 1 can skip past the answer — always keep the answer inside [lo, hi].
Sorting differs by language: Python sorts the tuples (p - r, p + r) with plain sort(); JavaScript and TypeScript need sort((x, y) => x[0] - y[0]) (without a compare function they compare as strings); Java Arrays.sort(lamps, (x, y) -> Long.compare(x[0], y[0])); C# Array.Sort(lamps, (x, y) => x.left.CompareTo(y.left)); C++ sorts std::pair directly with std::sort; in C qsort, returning x - y as an int from the compare function can overflow, so return (x > y) - (x < y).
Craykon passes a function returning negative, 0 or positive, as in lamps.Sort(byLeftEnd), and integer division is Mathf.Quot(lo + hi, 2).

Pythonmain.py · 40 lines
import sys data = sys.stdin.read().split()n, length, k = int(data[0]), int(data[1]), int(data[2])lamps = []  # (p - r, p + r): 늘이기 전에 비추는 구간for i in range(n):    p, r = int(data[3 + 2 * i]), int(data[4 + 2 * i])    lamps.append((p - r, p + r))lamps.sort()  # 모두 같은 t만큼 늘어나니 왼쪽 끝의 차례는 t와 상관없이 같다  def lamps_needed_ok(t):    """모든 등을 t만큼 늘였을 때 k개 이하로 [0, length]를 다 비출 수 있는가."""    need = 0  # 아직 비춘다고 확인하지 못한 가장 왼쪽 점    i = 0    best = -1  # 왼쪽 끝이 need 이하인 등 가운데 가장 멀리 가는 오른쪽 끝    used = 0    while used < k:        while i < n and lamps[i][0] - t <= need:  # 끝이 닿기만 해도 이어진다(닫힌 구간)            best = max(best, lamps[i][1] + t)            i += 1        if best < need:  # need를 비추는 등이 없다            return False        used += 1        if best >= length:            return True        if best == need:  # 더 나아갈 수 없다            return False        need = best    return False  lo, hi = 0, length  # t = length이면 어느 등 하나로도 [0, length]를 다 비춘다while lo < hi:    mid = (lo + hi) // 2    if lamps_needed_ok(mid):        hi = mid    else:        lo = mid + 1print(lo)
JavaScriptmain.js · 34 lines
const data = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const [n, length, k] = data;const lamps = []; // [p - r, p + r]: 늘이기 전에 비추는 구간for (let i = 0; i < n; i++) {  const p = data[3 + 2 * i], r = data[4 + 2 * i];  lamps.push([p - r, p + r]);}lamps.sort((x, y) => x[0] - y[0]); // 모두 같은 t만큼 늘어나니 왼쪽 끝의 차례는 t와 상관없이 같다 // 모든 등을 t만큼 늘였을 때 k개 이하로 [0, length]를 다 비출 수 있는가function canCover(t) {  let need = 0; // 아직 비춘다고 확인하지 못한 가장 왼쪽 점  let i = 0;  let best = -1; // 왼쪽 끝이 need 이하인 등 가운데 가장 멀리 가는 오른쪽 끝  for (let used = 1; used <= k; used++) {    while (i < n && lamps[i][0] - t <= need) { // 끝이 닿기만 해도 이어진다(닫힌 구간)      best = Math.max(best, lamps[i][1] + t);      i++;    }    if (best < need) return false; // need를 비추는 등이 없다    if (best >= length) return true;    if (best === need) return false; // 더 나아갈 수 없다    need = best;  }  return false;} let lo = 0, hi = length; // t = length이면 어느 등 하나로도 [0, length]를 다 비춘다while (lo < hi) {  const mid = Math.floor((lo + hi) / 2);  if (canCover(mid)) hi = mid;  else lo = mid + 1;}console.log(lo);
TypeScriptmain.ts · 36 lines
import { readFileSync } from "node:fs"; const data: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const [n, length, k] = data;const lamps: [left: number, right: number][] = []; // 늘이기 전에 비추는 구간 [p - r, p + r]for (let i = 0; i < n; i++) {  const p = data[3 + 2 * i], r = data[4 + 2 * i];  lamps.push([p - r, p + r]);}lamps.sort((x, y) => x[0] - y[0]); // 모두 같은 t만큼 늘어나니 왼쪽 끝의 차례는 t와 상관없이 같다 // 모든 등을 t만큼 늘였을 때 k개 이하로 [0, length]를 다 비출 수 있는가function canCover(t: number): boolean {  let need = 0; // 아직 비춘다고 확인하지 못한 가장 왼쪽 점  let i = 0;  let best = -1; // 왼쪽 끝이 need 이하인 등 가운데 가장 멀리 가는 오른쪽 끝  for (let used = 1; used <= k; used++) {    while (i < n && lamps[i][0] - t <= need) { // 끝이 닿기만 해도 이어진다(닫힌 구간)      best = Math.max(best, lamps[i][1] + t);      i++;    }    if (best < need) return false; // need를 비추는 등이 없다    if (best >= length) return true;    if (best === need) return false; // 더 나아갈 수 없다    need = best;  }  return false;} let lo = 0, hi = length; // t = length이면 어느 등 하나로도 [0, length]를 다 비춘다while (lo < hi) {  const mid: number = Math.floor((lo + hi) / 2);  if (canCover(mid)) hi = mid;  else lo = mid + 1;}console.log(lo);
JavaMain.java · 54 lines
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.Arrays;import java.util.StringTokenizer; public class Main {    static int n, k;    static long length;    static long[][] lamps; // {p - r, p + r}: 늘이기 전에 비추는 구간. p + r + t는 30억까지라 long     // 모든 등을 t만큼 늘였을 때 k개 이하로 [0, length]를 다 비출 수 있는가    static boolean canCover(long t) {        long need = 0; // 아직 비춘다고 확인하지 못한 가장 왼쪽 점        int i = 0;        long best = -1; // 왼쪽 끝이 need 이하인 등 가운데 가장 멀리 가는 오른쪽 끝        for (int used = 1; used <= k; used++) {            while (i < n && lamps[i][0] - t <= need) { // 끝이 닿기만 해도 이어진다(닫힌 구간)                best = Math.max(best, lamps[i][1] + t);                i++;            }            if (best < need) return false; // need를 비추는 등이 없다            if (best >= length) return true;            if (best == need) return false; // 더 나아갈 수 없다            need = best;        }        return false;    }     public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        StringTokenizer st = new StringTokenizer(in.readLine());        n = Integer.parseInt(st.nextToken());        length = Long.parseLong(st.nextToken());        k = Integer.parseInt(st.nextToken());        lamps = new long[n][];        for (int i = 0; i < n; i++) {            st = new StringTokenizer(in.readLine());            long p = Long.parseLong(st.nextToken());            long r = Long.parseLong(st.nextToken());            lamps[i] = new long[] {p - r, p + r};        }        // 모두 같은 t만큼 늘어나니 왼쪽 끝의 차례는 t와 상관없이 같다        Arrays.sort(lamps, (x, y) -> Long.compare(x[0], y[0]));         long lo = 0, hi = length; // t = length이면 어느 등 하나로도 [0, length]를 다 비춘다        while (lo < hi) {            long mid = (lo + hi) / 2;            if (canCover(mid)) hi = mid;            else lo = mid + 1;        }        System.out.println(lo);    }}
C#Main.cs · 55 lines
using System; class Program{    static int n, k;    static long length;    static (long left, long right)[] lamps; // 늘이기 전에 비추는 구간 [p - r, p + r]. p + r + t는 30억까지라 long     // 모든 등을 t만큼 늘였을 때 k개 이하로 [0, length]를 다 비출 수 있는가    static bool CanCover(long t)    {        long need = 0; // 아직 비춘다고 확인하지 못한 가장 왼쪽 점        int i = 0;        long best = -1; // 왼쪽 끝이 need 이하인 등 가운데 가장 멀리 가는 오른쪽 끝        for (int used = 1; used <= k; used++)        {            while (i < n && lamps[i].left - t <= need) // 끝이 닿기만 해도 이어진다(닫힌 구간)            {                best = Math.Max(best, lamps[i].right + t);                i++;            }            if (best < need) return false; // need를 비추는 등이 없다            if (best >= length) return true;            if (best == need) return false; // 더 나아갈 수 없다            need = best;        }        return false;    }     static void Main()    {        string[] first = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);        n = int.Parse(first[0]);        length = long.Parse(first[1]);        k = int.Parse(first[2]);        lamps = new (long left, long right)[n];        for (int i = 0; i < n; i++)        {            string[] w = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);            long p = long.Parse(w[0]), r = long.Parse(w[1]);            lamps[i] = (p - r, p + r);        }        // 모두 같은 t만큼 늘어나니 왼쪽 끝의 차례는 t와 상관없이 같다        Array.Sort(lamps, (x, y) => x.left.CompareTo(y.left));         long lo = 0, hi = length; // t = length이면 어느 등 하나로도 [0, length]를 다 비춘다        while (lo < hi)        {            long mid = (lo + hi) / 2;            if (CanCover(mid)) hi = mid;            else lo = mid + 1;        }        Console.WriteLine(lo);    }}
C++main.cpp · 48 lines
#include <algorithm>#include <iostream>#include <utility>#include <vector> int n, k;long long length;std::vector<std::pair<long long, long long>> lamps;  // (p - r, p + r): 늘이기 전에 비추는 구간. p + r + t는 30억까지라 long long // 모든 등을 t만큼 늘였을 때 k개 이하로 [0, length]를 다 비출 수 있는가bool canCover(long long t) {    long long need = 0;  // 아직 비춘다고 확인하지 못한 가장 왼쪽 점    int i = 0;    long long best = -1;  // 왼쪽 끝이 need 이하인 등 가운데 가장 멀리 가는 오른쪽 끝    for (int used = 1; used <= k; used++) {        while (i < n && lamps[i].first - t <= need) {  // 끝이 닿기만 해도 이어진다(닫힌 구간)            best = std::max(best, lamps[i].second + t);            i++;        }        if (best < need) return false;  // need를 비추는 등이 없다        if (best >= length) return true;        if (best == need) return false;  // 더 나아갈 수 없다        need = best;    }    return false;} int main() {    std::ios::sync_with_stdio(false);    std::cin.tie(nullptr);    std::cin >> n >> length >> k;    lamps.resize(n);    for (auto& lamp : lamps) {        long long p, r;        std::cin >> p >> r;        lamp = {p - r, p + r};    }    std::sort(lamps.begin(), lamps.end());  // 모두 같은 t만큼 늘어나니 왼쪽 끝의 차례는 t와 상관없이 같다     long long lo = 0, hi = length;  // t = length이면 어느 등 하나로도 [0, length]를 다 비춘다    while (lo < hi) {        long long mid = (lo + hi) / 2;        if (canCover(mid)) hi = mid;        else lo = mid + 1;    }    std::cout << lo << '\n';    return 0;}
Cmain.c · 55 lines
#include <stdio.h>#include <stdlib.h> #define MAXN 10000 typedef struct {    long long left, right; /* 늘이기 전에 비추는 구간 [p - r, p + r]. p + r + t는 30억까지라 long long */} Lamp; static Lamp lamps[MAXN];static int n, k;static long long length; static int byLeft(const void* a, const void* b) {    long long x = ((const Lamp*)a)->left, y = ((const Lamp*)b)->left;    return (x > y) - (x < y); /* x - y를 int로 돌려주면 넘칠 수 있다 */} /* 모든 등을 t만큼 늘였을 때 k개 이하로 [0, length]를 다 비출 수 있으면 1 */static int canCover(long long t) {    long long need = 0; /* 아직 비춘다고 확인하지 못한 가장 왼쪽 점 */    long long best = -1; /* 왼쪽 끝이 need 이하인 등 가운데 가장 멀리 가는 오른쪽 끝 */    int i = 0;    for (int used = 1; used <= k; used++) {        while (i < n && lamps[i].left - t <= need) { /* 끝이 닿기만 해도 이어진다(닫힌 구간) */            if (lamps[i].right + t > best) best = lamps[i].right + t;            i++;        }        if (best < need) return 0; /* need를 비추는 등이 없다 */        if (best >= length) return 1;        if (best == need) return 0; /* 더 나아갈 수 없다 */        need = best;    }    return 0;} int main(void) {    if (scanf("%d %lld %d", &n, &length, &k) != 3) return 0;    for (int i = 0; i < n; i++) {        long long p, r;        scanf("%lld %lld", &p, &r);        lamps[i].left = p - r;        lamps[i].right = p + r;    }    qsort(lamps, n, sizeof lamps[0], byLeft); /* 모두 같은 t만큼 늘어나니 왼쪽 끝의 차례는 t와 상관없이 같다 */     long long lo = 0, hi = length; /* t = length이면 어느 등 하나로도 [0, length]를 다 비춘다 */    while (lo < hi) {        long long mid = (lo + hi) / 2;        if (canCover(mid)) hi = mid;        else lo = mid + 1;    }    printf("%lld\n", lo);    return 0;}
Craykonmain.cray · 62 lines
// 모든 등을 t만큼 늘였을 때 k개 이하로 [0, 길이]를 다 비출 수 있는가비출수있나(lamps, 길이, k, t){    need = 0; // 아직 비춘다고 확인하지 못한 가장 왼쪽 점    i = 0;    best = -1; // 왼쪽 끝이 need 이하인 등 가운데 가장 멀리 가는 오른쪽 끝    for (used = 1; used <= k; used++)    {        while (i < lamps.Count && lamps[i][0] - t <= need)        {            best = Mathf.Max(best, lamps[i][1] + t);            i++;        }        if (best < need)        {            return false;        }        if (best >= 길이)        {            return true;        }        if (best == need)        {            return false;        }        need = best;    }    return false;} 왼쪽끝순(앞, 뒤){    return 앞[0] - 뒤[0];} 첫줄 = Console.ReadNumbers();n = 첫줄[0];길이 = 첫줄[1];k = 첫줄[2];lamps = []; // [p - r, p + r]: 늘이기 전에 비추는 구간for (j = 0; j < n; j++){    등 = Console.ReadNumbers();    lamps.Add([등[0] - 등[1], 등[0] + 등[1]]);}lamps.Sort(왼쪽끝순); // 모두 같은 t만큼 늘어나니 왼쪽 끝의 차례는 t와 상관없이 같다 lo = 0;hi = 길이; // t = 길이이면 어느 등 하나로도 [0, 길이]를 다 비춘다while (lo < hi){    mid = Mathf.Quot(lo + hi, 2);    if (비출수있나(lamps, 길이, k, mid))    {        hi = mid;    }    else    {        lo = mid + 1;    }}Console.WriteLine(lo);