Skip to content

rectangle_union_area.hpp

SECTIONGeometry INCLUDEnoya/rectangle_union_area.hpp

Exact union area of half-open axis-aligned integer rectangles by a sweep line and covered-length segment tree in O(n log n).

Verified by area_of_union_of_rectangles.

精确计算多个半开轴对齐整数矩形的并面积;适合覆盖面积需要去除重叠的题目。

Implementation

View on GitHub

#ifndef NOYA_RECTANGLE_UNION_AREA_HPP
#define NOYA_RECTANGLE_UNION_AREA_HPP 1

/// @complexity Time: O(n log n).
/// Space: O(n).

#include <algorithm>
#include <array>
#include <cassert>
#include <cstdint>
#include <numeric>
#include <vector>

namespace noya {

/// @brief Exact union area of half-open axis-aligned integer rectangles by a
/// sweep line and covered-length segment tree in O(n log n).
inline __int128 rectangle_union_area(
    const std::vector<std::array<std::int64_t, 4>> &rectangles) {
  struct event {
    std::int64_t x;
    std::int64_t lower;
    std::int64_t upper;
    int delta;
  };
  std::vector<event> events;
  std::vector<std::int64_t> coordinates;
  for (auto [left, right, lower, upper] : rectangles) {
    assert(left <= right && lower <= upper);
    if (left == right || lower == upper) {
      continue;
    }
    events.push_back({left, lower, upper, 1});
    events.push_back({right, lower, upper, -1});
    coordinates.push_back(lower);
    coordinates.push_back(upper);
  }
  if (events.empty()) {
    return 0;
  }
  std::sort(events.begin(), events.end(),
            [](const event &a, const event &b) { return a.x < b.x; });
  std::sort(coordinates.begin(), coordinates.end());
  coordinates.erase(std::unique(coordinates.begin(), coordinates.end()),
                    coordinates.end());
  int interval_count = int(coordinates.size()) - 1;
  std::vector<int> cover(interval_count * 4 + 4);
  std::vector<__int128> length(interval_count * 4 + 4);
  auto pull = [&](int node, int left, int right) {
    if (cover[node] > 0) {
      length[node] = __int128(coordinates[right]) - coordinates[left];
    } else if (right - left == 1) {
      length[node] = 0;
    } else {
      length[node] = length[node * 2] + length[node * 2 + 1];
    }
  };
  auto update = [&](auto &self, int node, int left, int right, int query_left,
                    int query_right, int delta) -> void {
    if (query_right <= left || right <= query_left) {
      return;
    }
    if (query_left <= left && right <= query_right) {
      cover[node] += delta;
      pull(node, left, right);
      return;
    }
    int middle = std::midpoint(left, right);
    self(self, node * 2, left, middle, query_left, query_right, delta);
    self(self, node * 2 + 1, middle, right, query_left, query_right, delta);
    pull(node, left, right);
  };

  __int128 area = 0;
  std::int64_t previous_x = events[0].x;
  for (int index = 0; index < int(events.size());) {
    std::int64_t current_x = events[index].x;
    area += (__int128(current_x) - previous_x) * length[1];
    while (index < int(events.size()) && events[index].x == current_x) {
      int lower = int(std::lower_bound(coordinates.begin(), coordinates.end(),
                                       events[index].lower) -
                      coordinates.begin());
      int upper = int(std::lower_bound(coordinates.begin(), coordinates.end(),
                                       events[index].upper) -
                      coordinates.begin());
      update(update, 1, 0, interval_count, lower, upper, events[index].delta);
      index++;
    }
    previous_x = current_x;
  }
  return area;
}

} // namespace noya

#endif // NOYA_RECTANGLE_UNION_AREA_HPP
#include <algorithm>
#include <array>
#include <cassert>
#include <cstdint>
#include <numeric>
#include <vector>

/// @complexity Time: O(n log n).
/// Space: O(n).

namespace noya {

/// @brief Exact union area of half-open axis-aligned integer rectangles by a
/// sweep line and covered-length segment tree in O(n log n).
inline __int128 rectangle_union_area(
    const std::vector<std::array<std::int64_t, 4>> &rectangles) {
  struct event {
    std::int64_t x;
    std::int64_t lower;
    std::int64_t upper;
    int delta;
  };
  std::vector<event> events;
  std::vector<std::int64_t> coordinates;
  for (auto [left, right, lower, upper] : rectangles) {
    assert(left <= right && lower <= upper);
    if (left == right || lower == upper) {
      continue;
    }
    events.push_back({left, lower, upper, 1});
    events.push_back({right, lower, upper, -1});
    coordinates.push_back(lower);
    coordinates.push_back(upper);
  }
  if (events.empty()) {
    return 0;
  }
  std::sort(events.begin(), events.end(),
            [](const event &a, const event &b) { return a.x < b.x; });
  std::sort(coordinates.begin(), coordinates.end());
  coordinates.erase(std::unique(coordinates.begin(), coordinates.end()),
                    coordinates.end());
  int interval_count = int(coordinates.size()) - 1;
  std::vector<int> cover(interval_count * 4 + 4);
  std::vector<__int128> length(interval_count * 4 + 4);
  auto pull = [&](int node, int left, int right) {
    if (cover[node] > 0) {
      length[node] = __int128(coordinates[right]) - coordinates[left];
    } else if (right - left == 1) {
      length[node] = 0;
    } else {
      length[node] = length[node * 2] + length[node * 2 + 1];
    }
  };
  auto update = [&](auto &self, int node, int left, int right, int query_left,
                    int query_right, int delta) -> void {
    if (query_right <= left || right <= query_left) {
      return;
    }
    if (query_left <= left && right <= query_right) {
      cover[node] += delta;
      pull(node, left, right);
      return;
    }
    int middle = std::midpoint(left, right);
    self(self, node * 2, left, middle, query_left, query_right, delta);
    self(self, node * 2 + 1, middle, right, query_left, query_right, delta);
    pull(node, left, right);
  };

  __int128 area = 0;
  std::int64_t previous_x = events[0].x;
  for (int index = 0; index < int(events.size());) {
    std::int64_t current_x = events[index].x;
    area += (__int128(current_x) - previous_x) * length[1];
    while (index < int(events.size()) && events[index].x == current_x) {
      int lower = int(std::lower_bound(coordinates.begin(), coordinates.end(),
                                       events[index].lower) -
                      coordinates.begin());
      int upper = int(std::lower_bound(coordinates.begin(), coordinates.end(),
                                       events[index].upper) -
                      coordinates.begin());
      update(update, 1, 0, interval_count, lower, upper, events[index].delta);
      index++;
    }
    previous_x = current_x;
  }
  return area;
}

} // namespace noya