Official

G - Push Simultaneously Editorial by en_translator


When each persons have decided which bush to press finally, his optimal move is to first head straight toward their destination and stay there once he arrived.

Thus, the answer is a distance between a person and a button.

Here, if one can solve the following problem fast enough, the answer can be found by a binary search on at most \(N ^ 2\) candidates.

Given a positive real value \(t\), determine if one can take a bijection from the people to the buttons so that the distance between any person to the corresponding button is most \(t\).

Such a bijection exists if and only if the size of the maximum matching, in a bipartite graph obtained by adding edges between a person and a button distant at most by \(t\), is equal to \(N\).

The maximum matching in a bipartite graph can be obtained in \(O(E\sqrt V)\) time with Hopcroft–Karp algorithm or Dinic’s algorithm, which enables us to solve the original problem in a total of \(O(N ^ {2.5}\log N)\) time.

The sample code can be found below.

Regarding the precision errors, the coordinate difference given as the arguments of hypot function has an error of at most \(0.5\operatorname{ulp}\), and the computation of hypot function it self yields at most \(1\operatorname{ulp}\) error, and no other part causes an error, so the obtained answer is precise enough.

#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <ranges>
#include <iomanip>
#include <atcoder/maxflow>

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

    vector<pair<unsigned long, unsigned long>> start(N), goal(N);
    for (auto&& [x, y] : start) cin >> x >> y;
    for (auto&& [x, y] : goal) cin >> x >> y;

    vector<tuple<double, unsigned, unsigned>> edges;
    for (unsigned i = 0; const auto& [sx, sy] : start) {
        for (unsigned j = 0; const auto& [gx, gy] : goal) {
            // (Distance between (sx, sy) and (gx, gy), person index, button index)
            edges.emplace_back(hypot(max(sx, gx) - min(sx, gx), max(sy, gy) - min(sy, gy)), i, j);
            ++j;
        }
        ++i;
    }

    // Whether there is an matching with edges of lengths at most X
    const auto judge = [N, &edges](const auto& X) {
        atcoder::mf_graph<long long> graph(1 + N + N + 1);
        for (unsigned i = 0; i < N; ++i) graph.add_edge(0, 1 + i, 1);
        for (unsigned i = 0; i < N; ++i) graph.add_edge(1 + N + i, 1 + N + N + 0, 1);

        // Add edges with length at most X to the graph
        for (const auto& [w, u, v] : edges | views::filter([X](const auto& e) { return get<0>(e) <= X; }))
            graph.add_edge(1 + u, 1 + N + v, 1);

        // Check if the matching size is N
        return graph.flow(0, 1 + N + N + 0) < N;
    };

    // Sort by length and binary search
    ranges::sort(edges);
    cout << setprecision(7) << *ranges::partition_point(edges | views::keys, judge) << endl;
    return 0;
}

posted:
last update: