diff --git a/docs/structure/rectangle-apply-point-get.md b/docs/structure/rectangle-apply-point-get.md new file mode 100644 index 0000000..8724671 --- /dev/null +++ b/docs/structure/rectangle-apply-point-get.md @@ -0,0 +1,54 @@ +--- +title: Rectangle Apply Point Get +documentation_of: //structure/rectangle_apply_point_get.hpp +--- + +長方形領域に作用素を掛け、一点に掛かっている作用素を取得するデータ構造です。 + +座標範囲を先に指定し、その範囲内の長方形作用と一点取得をオンラインに処理できます。 + +作用素 `F` はモノイドとして扱います。 + +``` +F composition(F f, F g) +F id() +``` + +`composition(f, g)` は ACL の `lazy_segtree` と同じく、作用 `g` の後に作用 `f` を適用する合成、`id()` は単位元です。 + +## コンストラクタ + +``` +rectangle_apply_point_get(T xl, T xr, T yl, T yr) +``` + +座標範囲 $[xl, xr) \times [yl, yr)$ を指定する。 +`T` は座標の型です。 + +## apply + +``` +void apply(T l, T r, T d, T u, F f) +``` + +領域 $[l, r) \times [d, u)$ に作用 `f` を掛ける。 + +### 計算量 +座標範囲の幅を $X = xr - xl, Y = yr - yl$ として +- $O(\log X \log Y)$ + +新しく作られる内部ノード数も $O(\log X \log Y)$。 + +## get + +``` +F get(T x, T y) +``` + +点 `(x, y)` に掛かっている作用素の合成を返す。 + +### 計算量 +座標範囲の幅を $X = xr - xl, Y = yr - yl$ として +- $O(\log X \log Y)$ + +内部ノードは作らない。 diff --git a/structure/rectangle_apply_point_get.hpp b/structure/rectangle_apply_point_get.hpp new file mode 100644 index 0000000..bd4ca50 --- /dev/null +++ b/structure/rectangle_apply_point_get.hpp @@ -0,0 +1,114 @@ +#ifndef HARUILIB_STRUCTURE_RECTANGLE_APPLY_POINT_GET_HPP +#define HARUILIB_STRUCTURE_RECTANGLE_APPLY_POINT_GET_HPP + +#include +#include + +template +struct rectangle_apply_point_get { + rectangle_apply_point_get(T xl, T xr, T yl, T yr) + : xl(xl), xr(xr), yl(yl), yr(yr), root(-1) { + assert(xl < xr); + assert(yl < yr); + root = new_x_node(); + } + + void apply(T l, T r, T d, T u, F f) { + assert(xl <= l && l <= r && r <= xr); + assert(yl <= d && d <= u && u <= yr); + apply_x(root, xl, xr, l, r, d, u, f); + } + + F get(T x, T y) const { + assert(xl <= x && x < xr); + assert(yl <= y && y < yr); + return get_x(root, xl, xr, x, y); + } + +private: + struct XNode { + int left, right, y_root; + }; + + struct YNode { + int left, right; + F val; + }; + + int new_x_node() { + xs.push_back({-1, -1, -1}); + return (int)xs.size() - 1; + } + + int new_y_node() { + ys.push_back({-1, -1, id()}); + return (int)ys.size() - 1; + } + + void apply_x(int k, T nl, T nr, T ql, T qr, T qd, T qu, F f) { + if (qr <= nl || nr <= ql) return; + if (ql <= nl && nr <= qr) { + xs[k].y_root = apply_y(xs[k].y_root, yl, yr, qd, qu, f); + return; + } + + T mid = nl + (nr - nl) / 2; + if (ql < mid) { + if (xs[k].left == -1) { + int child = new_x_node(); + xs[k].left = child; + } + apply_x(xs[k].left, nl, mid, ql, qr, qd, qu, f); + } + if (mid < qr) { + if (xs[k].right == -1) { + int child = new_x_node(); + xs[k].right = child; + } + apply_x(xs[k].right, mid, nr, ql, qr, qd, qu, f); + } + } + + int apply_y(int k, T nl, T nr, T ql, T qr, F f) { + if (qr <= nl || nr <= ql) return k; + if (k == -1) k = new_y_node(); + if (ql <= nl && nr <= qr) { + ys[k].val = composition(f, ys[k].val); + return k; + } + + T mid = nl + (nr - nl) / 2; + if (ql < mid) ys[k].left = apply_y(ys[k].left, nl, mid, ql, qr, f); + if (mid < qr) ys[k].right = apply_y(ys[k].right, mid, nr, ql, qr, f); + return k; + } + + F get_x(int k, T nl, T nr, T x, T y) const { + if (k == -1) return id(); + + F res = get_y(xs[k].y_root, yl, yr, y); + if (nr - nl == 1) return res; + + T mid = nl + (nr - nl) / 2; + if (x < mid) return composition(get_x(xs[k].left, nl, mid, x, y), res); + return composition(get_x(xs[k].right, mid, nr, x, y), res); + } + + F get_y(int k, T nl, T nr, T y) const { + if (k == -1) return id(); + + F res = ys[k].val; + if (nr - nl == 1) return res; + + T mid = nl + (nr - nl) / 2; + if (y < mid) return composition(get_y(ys[k].left, nl, mid, y), res); + return composition(get_y(ys[k].right, mid, nr, y), res); + } + + T xl, xr, yl, yr; + int root; + std::vector xs; + std::vector ys; +}; + +#endif // HARUILIB_STRUCTURE_RECTANGLE_APPLY_POINT_GET_HPP diff --git a/test/verify/structure/yosupo-rectangle-add-point-get.test.cpp b/test/verify/structure/yosupo-rectangle-add-point-get.test.cpp new file mode 100644 index 0000000..f5c1316 --- /dev/null +++ b/test/verify/structure/yosupo-rectangle-add-point-get.test.cpp @@ -0,0 +1,84 @@ +#define PROBLEM "https://judge.yosupo.jp/problem/rectangle_add_point_get" +#include "template/template.hpp" +#include "structure/rectangle_apply_point_get.hpp" + +#include +#include + +ll composition(ll f, ll g) { + return f + g; +} + +ll id() { + return 0; +} + +struct Rect { + int l, d, r, u; + ll w; +}; + +struct Query { + int type; + Rect rect; + int x, y; +}; + +int main() { + ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); + + int N, Q; cin >> N >> Q; + vector rects(N); + for (int i=0; i> rects[i].l >> rects[i].d >> rects[i].r >> rects[i].u >> rects[i].w; + } + + vector qs(Q); + vector xs, ys; + for (int q=0; q> qs[q].type; + if (qs[q].type == 0) { + cin >> qs[q].rect.l >> qs[q].rect.d >> qs[q].rect.r >> qs[q].rect.u >> qs[q].rect.w; + } + else { + cin >> qs[q].x >> qs[q].y; + xs.push_back(qs[q].x); + ys.push_back(qs[q].y); + } + } + + if (xs.empty()) return 0; + + sort(xs.begin(), xs.end()); + xs.erase(unique(xs.begin(), xs.end()), xs.end()); + sort(ys.begin(), ys.end()); + ys.erase(unique(ys.begin(), ys.end()), ys.end()); + + auto get_x = [&](int x) { + return lower_bound(xs.begin(), xs.end(), x) - xs.begin(); + }; + auto get_y = [&](int y) { + return lower_bound(ys.begin(), ys.end(), y) - ys.begin(); + }; + auto apply = [&](rectangle_apply_point_get& g, const Rect& rect) { + int l = get_x(rect.l); + int r = get_x(rect.r); + int d = get_y(rect.d); + int u = get_y(rect.u); + g.apply(l, r, d, u, rect.w); + }; + + rectangle_apply_point_get g(0, (int)xs.size(), 0, (int)ys.size()); + for (auto& rect : rects) apply(g, rect); + + for (auto& q : qs) { + if (q.type == 0) { + apply(g, q.rect); + } + else { + cout << g.get(get_x(q.x), get_y(q.y)) << "\n"; + } + } + + return 0; +}