Official

E - Last Train Editorial by en_translator


Reverse the time evolution and regard that there is a train departing from station \(B _ i\) at time \(-t-c _ i\) and arriving at station \(A _ i\) at time \(-t\).
Then, the problem to solve can be expressed as follows:

  • Suppose that you are at station \(N\) sufficiently early (for example, at time \(-2\times10 ^ {18}\). Find the minimum time \(-f(S)\) at which one can leave there to reach station \(S\). (If one cannot arrive at station \(S\), let \(f(S)=-\infty\).)

This problem can be solved by only inspecting the train that departs earliest after time \(-f(S)\) from station \(B _ i\) (taking later trains does not improve the solution).

Thus, just as in Dijkstra’s algorithm, one can successively inspect the station for which one can arrive the earliest among those not inspected yet.

The sample code is as follows. In this problem, we do not reverse the time, and instead explore the station from which one can depart latest.

#include <iostream>
#include <utility>
#include <optional>
#include <vector>
#include <queue>
#include <limits>
#include <ranges>

int main() {
    using namespace std;
    unsigned N, M;
    cin >> N >> M;

    // Structure that stores information of trains
    struct train {
        unsigned from, to;
        unsigned long first_train;
        unsigned long interval;
        unsigned long count;
        unsigned long distance;

        // Finds the minimum departure time that arrives at station `to` by time `time_limit`
        // Returns nullopt if no such train exists
        optional<unsigned long> get_last_train(unsigned long time_limit) const {
            if (time_limit < first_train + distance)
                return nullopt;
            return first_train + min(count - 1, (time_limit - first_train - distance) / interval) * interval;
        }
    };
    vector<vector<train>> train_info(N);
    for (const auto _ : views::iota(0U, M)) {
        unsigned long l, d, k, c;
        unsigned A, B;
        cin >> l >> d >> k >> c >> A >> B;
        train_info[--B].push_back({--A, --B, l, d, k, c});
    }

    priority_queue<pair<unsigned long, unsigned>> pq;
    vector<unsigned long> ans(N);

    // Start from being at station N sufficiently late
    pq.emplace(ans.back() = numeric_limits<unsigned long>::max(), N - 1);
    while (!empty(pq)) {
        // Find the station at which one can stay until latest time
        const auto[time_limit, now]{pq.top()};
        pq.pop();
        if (ans[now] > time_limit)continue;

        // For information on trains arriving at station `now`
        for (const auto &e : train_info[now]) {
            const auto next{e.from};
            const auto next_time{e.get_last_train(time_limit)};
            // 駅 next の最終時刻 ≥ 駅 now の最終時刻に間に合う電車の発射時刻
            // Last time at which one can be at station `next` ≥ the departure time of the train that manages station `now` at the latest time possible
            if (next_time && ans[next] < *next_time) // If updatable
                pq.emplace(ans[next] = *next_time, next); // Insert to the priority queue
        }
    }

    for (const auto d : ans | views::take(N - 1))
        if (d)
            cout << d << endl;
        else
            cout << "Unreachable" << endl;

    return 0;
}

posted:
last update: