Official

D - Cross Explosion Editorial by en_translator


First of all, you can find the correct answer by processing the queries, simply following the instructions in the problem statement. However, if you use only an array to manage the remaining walls, the time complexity would be \(\mathrm{O}(H^2 W^2)\) or \(\mathrm{O}(HW (H+W))\), not finishing within the execution time limit. Instead, a good use of data structures is required.

Consider managing the remaining wall in an ordered set (std::set in C++). Specifically, we manage the following \(g_1\) and \(g_2\):

  • \(g_1\): an array whose \(i\)-th element is an ordered set maintaining the column index the remaining walls in the \(i\)-th row;
  • \(g_1\): an array whose \(j\)-th element is an ordered set maintaining the row index the remaining walls in the \(j\)-th column;

By constructing such a data structure and appropriately using binary searching in an ordered set (set::lower_bound in C++), a query can be processed in \(\mathrm{O}(\log H + \log W)\) time. (For more details, please refer to the sample code.)
Therefore, this problem can be solved in a total of \(\mathrm{O}((HW + Q) (\log H + \log W))\) time, which can be finished within the execution time limit with enough margin.

The following is sample code in C++. A Python implementation that uses SortedSet by tatyam can be found here.

#include <cassert>
#include <iostream>
#include <set>
#include <vector>
using namespace std;

int main() {
  cin.tie(nullptr);
  ios::sync_with_stdio(false);

  int H, W, Q;
  cin >> H >> W >> Q;

  vector<set<int>> g1(H), g2(W);
  for (int i = 0; i < H; i++) {
    for (int j = 0; j < W; j++) {
      g1[i].insert(j);
      g2[j].insert(i);
    }
  }

  auto erase = [&](int i, int j) { g1[i].erase(j), g2[j].erase(i); };

  while (Q--) {
    int R, C;
    cin >> R >> C;
    --R, --C;

    if (g1[R].count(C)) {
      erase(R, C);
      continue;
    }

    // up
    {
      auto it = g2[C].lower_bound(R);
      if (it != begin(g2[C])) erase(*prev(it), C);
    }
    // down
    {
      auto it = g2[C].lower_bound(R);
      if (it != end(g2[C])) erase(*it, C);
    }
    // left
    {
      auto it = g1[R].lower_bound(C);
      if (it != begin(g1[R])) erase(R, *prev(it));
    }
    // right
    {
      auto it = g1[R].lower_bound(C);
      if (it != end(g1[R])) erase(R, *it);
    }
  }

  int ans = 0;
  for (int i = 0; i < H; i++) ans += g1[i].size();
  cout << ans << "\n";
}

posted:
last update: