DSA sheet / Sorting and searching

Concert Tickets

medium about 30 min sortingbinary-searchmultisetunion-find
Open on CSES

Do these first: Apartments

The problem in brief

There are n tickets with given prices and m customers (both up to 200 000). Customers arrive one by one, each with a maximum price they are willing to pay. Each customer buys the most expensive remaining ticket priced at most their maximum, or nothing if none exists. For each customer print the price paid, or -1.

Try it

Tab indents; press Esc, then Tab, to leave the box. Ctrl+Enter runs.

Hints

Stuck? Reveal one hint at a time. Each nudges without giving away the next.

The walkthrough

Spoilers ahead: open a section only after you have given the hints a fair try.

How to think

The simulation is straightforward: process customers in order, pick the best ticket, remove it. Doing that with a linear scan is O(n * m) and too slow. What you need is a data structure supporting two operations in about O(log n): find the largest element at most t, and delete it. That is exactly an ordered multiset.

Where languages lack one, you can build the same behaviour from simpler parts: sort the prices once, binary search for the position of the last price at most t, and keep a “previous free slot” pointer with union-find so already-sold tickets are skipped in near-constant time. This trade (replace deletion with skipping) is a powerful trick whenever removals only ever shrink a sorted collection.

The habit: name the abstract operations your algorithm needs (predecessor query, delete), then choose the data structure that offers them.

Intuition

Tickets sit on a shelf sorted by price. A customer with budget t walks to the last ticket that is not more expensive than t, buys it, and leaves a gap. Later customers standing at the same place slide left over gaps to the next real ticket.

Approach

C++ and Java: keep the tickets in an ordered multiset (multiset / TreeMap with counts). For each customer take the greatest key at most t, output it and remove one copy; output -1 if there is none.

Python and JavaScript below: sort the prices, and give every slot a pointer p where p[k] == k means slot k is still available and otherwise p[k] points left. For a customer, binary search how many prices are at most t, then find the nearest available slot at or left of that count; slot 0 is a sentinel meaning “none”. Selling slot k sets p[k] = k - 1.

Pitfalls: the same price can appear many times, so each copy is separate; prices and budgets up to 10^9 fit in 32-bit signed ints. Output with one buffered write.

Complexity

O((n + m) log n) time. O(n) memory.

Solutions

Written from scratch and checked by compiling and running each one against a brute force on random inputs. Fast input/output, the way you would submit it.

Show solutions (C++17, Python 3, Java 17, Node.js)
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    multiset<int> tickets;
    for (int i = 0; i < n; i++) {
        int h;
        cin >> h;
        tickets.insert(h);
    }
    string out;
    for (int i = 0; i < m; i++) {
        int t;
        cin >> t;
        auto it = tickets.upper_bound(t);
        if (it == tickets.begin()) {
            out += "-1\n";
        } else {
            --it;
            out += to_string(*it) + "\n";
            tickets.erase(it);
        }
    }
    cout << out;
    return 0;
}

Problem: CSES Problem Set (Antti Laaksonen, University of Helsinki), CC BY-NC-SA 4.0. The explanations and code above are original (© Anupam Kumar).

Related problems

Back to the sheet