diff --git a/data_structure/area_of_union_of_rectangles.hpp b/data_structure/area_of_union_of_rectangles.hpp new file mode 100644 index 00000000..31b9c20c --- /dev/null +++ b/data_structure/area_of_union_of_rectangles.hpp @@ -0,0 +1,72 @@ +#pragma once +#include +#include + +#include "segmenttree/acl_lazysegtree.hpp" + +template class AreaOfUnionOfRectangles { + struct Rectangle { + T xl, xr, yl, yr; + }; + std::vector rectangles; + + struct S { + int min = 1 << 30; + T width = T{}; + T min_width = T{}; + T nonzero_width() const { return width - (min == 0) * min_width; } + }; + static S op(S l, S r) { + T min_width = (l.min <= r.min) * l.min_width + (l.min >= r.min) * r.min_width; + return {std::min(l.min, r.min), l.width + r.width, min_width}; + } + static S e() { return S{}; } + using F = int; + static S mapping(F f, S x) { return {x.min + f, x.width, x.min_width}; } + static F composition(F f, F g) { return f + g; } + static F id() { return 0; } + +public: + void add_rectangle(T xl, T xr, T yl, T yr) { rectangles.push_back(Rectangle{xl, xr, yl, yr}); } + + T solve() const { + std::vector xs; + xs.reserve(rectangles.size() * 2); + for (auto r : rectangles) xs.push_back(r.xl), xs.push_back(r.xr); + std::sort(xs.begin(), xs.end()); + xs.erase(std::unique(xs.begin(), xs.end()), xs.end()); + + if (xs.size() <= 1) return T{}; + + auto seg = [&]() { + std::vector inits; + inits.reserve((int)xs.size() - 1); + for (int i = 0; i < (int)xs.size() - 1; ++i) { + inits.emplace_back(S{0, xs[i + 1] - xs[i], xs[i + 1] - xs[i]}); + } + return atcoder::lazy_segtree{inits}; + }(); + + std::vector> updates; + updates.reserve(rectangles.size() * 2); + for (auto r : rectangles) { + const int ixl = std::lower_bound(xs.begin(), xs.end(), r.xl) - xs.begin(); + const int ixr = std::lower_bound(xs.begin(), xs.end(), r.xr) - xs.begin(); + updates.emplace_back(r.yl, ixl, ixr, true); + updates.emplace_back(r.yr, ixl, ixr, false); + } + + std::sort(updates.begin(), updates.end()); + T current_y = std::get<0>(updates.front()); + T res{0}; + for (auto [y, il, ir, tp] : updates) { + if (y != current_y) { + res += (y - current_y) * seg.all_prod().nonzero_width(); + current_y = y; + } + seg.apply(il, ir, tp ? 1 : -1); + } + + return res; + } +}; diff --git a/data_structure/area_of_union_of_rectangles.md b/data_structure/area_of_union_of_rectangles.md new file mode 100644 index 00000000..98f00dd5 --- /dev/null +++ b/data_structure/area_of_union_of_rectangles.md @@ -0,0 +1,21 @@ +--- +title: Area of union of rectangles (2 次元平面上の長方形たちが覆う面積) +documentation_of: ./area_of_union_of_rectangles.hpp +--- + +与えられた 2 次元平面上の各辺が $x$ 軸や $y$ 軸に平行な $n$ 個の長方形たちに対して,これらの和集合の面積を $O(n \log n)$ で計算する. + +## 使用方法 + +```cpp +AreaOfUnionOfRectangles aur; + +long long xl, xr, yl, yr; +aur.add_rectangle(xl, xr, yl, yr); + +cout << aur.solve() << '\n'; +``` + +## 問題例 + +- [Library Checker: Area of Union of Rectangles](https://judge.yosupo.jp/problem/area_of_union_of_rectangles) diff --git a/data_structure/test/area_of_union_of_rectangles.test.cpp b/data_structure/test/area_of_union_of_rectangles.test.cpp new file mode 100644 index 00000000..79ac8e57 --- /dev/null +++ b/data_structure/test/area_of_union_of_rectangles.test.cpp @@ -0,0 +1,21 @@ +#define PROBLEM "https://judge.yosupo.jp/problem/area_of_union_of_rectangles" +#include "data_structure/area_of_union_of_rectangles.hpp" + +#include +using namespace std; + +int main() { + cin.tie(nullptr), ios::sync_with_stdio(false); + + int N; + cin >> N; + AreaOfUnionOfRectangles aur; + + while (N--) { + int l, d, r, u; + cin >> l >> d >> r >> u; + aur.add_rectangle(l, r, d, u); + } + + cout << aur.solve() << '\n'; +}