Official

E - Distribute Bunnies Editorial by en_translator


Although there seems various approaches for this problem, greedy or DP (Dynamic Programming) algorithms are not effective in any way.
The key is to reinterpret it as a graph theory problem.
We will introduce several solutions.

Solution 1

Consider the graph where each coordinate \(x\) is corresponded to vertex \(x\). For each rabbit \(i\), add an edge between the two vertices that a rabbit can move to. Specifically, we connect vertices \((X_i - R_i)\) and \((X_i + R_i)\).
In this graph, the problem can be represented as follows:

  • Consider for each edge painting one of its endpoint. (The same vertex may be painted multiple times.) Maximize the number of vertices that are painted once or more.

Consider solving this problem. Since this problem can be solved independently for each connected component, consider for simplicity the case where the graph has \(n\) vertices and \(m\) edges.
It turns out that we can consider the cases separately where the graph is tree and where it is not separately. Let us take a closer look:

1. When the graph is a tree (when \(n=m+1\)):

When the graph is a tree, the connected component contains \(m=n-1\) edges, so at most \((n-1)\) vertices can be painted. Also, when we take any vertex as a root to consider the graph as a rooted tree, and paint the child vertex for each edge, we can always paint \((n-1)\) vertices. (If you do not understand, try drawing a diagram.) Therefore, in this case the maximum number is \((n-1)\), which is always achievable.

2. When the graph is not a tree (when \(m \geq n\)):

When the graph is not a tree, the connected components contains \(n\) vertices, so at most \(n\) vertices can be painted. Also, we can always achieve this number by the following procedure:

  • Take a spanning tree \(T\) of \(G\).
  • There always exists an edge contained in \(G\) and not contained in \(T\); take one such edge. Suppose that the edge connects \(u-v\).
  • Regard \(T\) as a rooted tree rooted at vertex \(v\), and for each edge paint the child vertex. Now all but vertex \(v\) is painted.
  • Use the \(u-v\) edge to paint vertex \(v\).

Therefore, in this case the number is \(n\), which is always achievable.

Hence, the answer has been found for both cases where the connected component is and is not a tree. Thus the problem can be solved by the following steps:

  • Construct a graph as mentioned above.
  • Let \(\mathrm{ans}\) be the variable that stores the answer. Initially, \(\mathrm{ans} = 0\).
  • Enumerate the connected components containing at least one edge. For each connected component, determine if it is a tree. If it is, add \((\text{the number of vertices}) - 1\) to the answer; otherwise, add \((\text{the number of vertices})\) to the answer.
  • The final value of \(\mathrm{ans}\) is the answer.

The computational complexity is \(\mathrm{O}(N \log N)\) when the graph is stored in a map, and this complexity is fast enough.

Solution 2

To be translated.

  • Sample implementation of Solution 1 (C++)
#include <iostream>
#include <map>
#include <set>
#include <utility>
#include <vector>
using namespace std;

int main() {
  cin.tie(0)->sync_with_stdio(0);
  int N;
  cin >> N;
  vector<int> X(N), R(N);
  for (int i = 0; i < N; i++) cin >> X[i] >> R[i];

  map<int, vector<pair<int, int>>> g;
  for (int i = 0; i < N; i++) {
    int xpr = X[i] + R[i];
    int xmr = X[i] - R[i];
    g[xpr].emplace_back(xmr, i);
    g[xmr].emplace_back(xpr, i);
  }

  set<int> visited;
  auto dfs = [&](auto rc, int c, int edge_num) -> pair<int, int> {
    visited.insert(c);
    int num = 1;
    int is_tree = true;
    for (auto& [d, e] : g[c]) {
      if (e == edge_num) continue;
      if (visited.count(d)) {
        is_tree = false;
      } else {
        auto [n, f] = rc(rc, d, e);
        num += n;
        is_tree &= f;
      }
    }
    return make_pair(num, is_tree);
  };

  int ans = 0;
  for (auto& [key, _] : g) {
    if (visited.count(key)) continue;
    auto [num, is_tree] = dfs(dfs, key, -1);
    ans += is_tree ? num - 1 : num;
  }
  cout << ans << "\n";
}
  • Sample implementation of Solution 2 (C++)
#include <iostream>
#include <set>
#include <utility>
#include <vector>
using namespace std;
#include "atcoder/maxflow.hpp"

int main() {
  cin.tie(0)->sync_with_stdio(0);
  int N;
  cin >> N;
  vector<int> X(N), R(N);
  for (int i = 0; i < N; i++) cin >> X[i] >> R[i];

  set<int> st;
  for (int i = 0; i < N; i++) {
    int xpr = X[i] + R[i];
    int xmr = X[i] - R[i];
    st.insert(xpr), st.insert(xmr);
  }

  vector<int> coords;
  for (auto& s : st) coords.push_back(s);
  int M = coords.size();

  int source = N + M;
  int sink = source + 1;
  atcoder::mf_graph<int> g(sink + 1);

  for (int i = 0; i < N; i++) {
    g.add_edge(source, i, 1);
  }
  for (int i = 0; i < N; i++) {
    int xpr = X[i] + R[i];
    int xmr = X[i] - R[i];
    xpr = lower_bound(begin(coords), end(coords), xpr) - begin(coords);
    xmr = lower_bound(begin(coords), end(coords), xmr) - begin(coords);
    g.add_edge(i, N + xpr, 1);
    g.add_edge(i, N + xmr, 1);
  }
  for (int j = 0; j < M; j++) {
    g.add_edge(N + j, sink, 1);
  }

  cout << g.flow(source, sink) << "\n";
}

posted:
last update: