NicheLibrary

This documentation is automatically generated by NotLeonian/competitive-verifier (forked from competitive-verifier/competitive-verifier)

View the Project on GitHub NotLeonian/NicheLibrary

:heavy_check_mark: verify/standalone-rectangle-add-max-get.test.cpp

Depends on

Code

// competitive-verifier: STANDALONE

#include <algorithm>
#include <cassert>
#include <limits>
#include <tuple>
#include <utility>
#include <vector>

#include "../other/rectangle-add-max-get.hpp"

namespace {
struct TestRectangle {
    int l, d, r, u;
    long long w;
};

struct BruteResult {
    long long max_value;
    int minimum_x;
    int minimum_y;
    int maximum_x;
    int maximum_y;
    long long area;
};

BruteResult brute_force_rectangle(const std::vector<TestRectangle> &rectangles,
                                  int l, int d, int r, int u) {
    bool found = false;
    BruteResult ret{};
    for (int x = l; x < r; ++x) {
        for (int y = d; y < u; ++y) {
            long long value = 0;
            for (const auto &rect : rectangles) {
                if (rect.l <= x && x < rect.r && rect.d <= y && y < rect.u) {
                    value += rect.w;
                }
            }
            if (!found || ret.max_value < value) {
                ret = BruteResult{value, x, y, x, y, 1};
                found = true;
            } else if (ret.max_value == value) {
                ++ret.area;
                if (std::make_pair(x, y) <
                    std::make_pair(ret.minimum_x, ret.minimum_y)) {
                    ret.minimum_x = x;
                    ret.minimum_y = y;
                }
                if (std::make_pair(ret.maximum_x, ret.maximum_y) <
                    std::make_pair(x, y)) {
                    ret.maximum_x = x;
                    ret.maximum_y = y;
                }
            }
        }
    }
    assert(found);
    return ret;
}

template <class Lower, class Upper>
BruteResult brute_force_variable(const std::vector<TestRectangle> &rectangles,
                                 int l, int r, Lower lower_y, Upper upper_y) {
    bool found = false;
    BruteResult ret{};
    for (int x = l; x < r; ++x) {
        const int d = lower_y(x);
        const int u = upper_y(x);
        assert(d <= u);
        for (int y = d; y < u; ++y) {
            long long value = 0;
            for (const auto &rect : rectangles) {
                if (rect.l <= x && x < rect.r && rect.d <= y && y < rect.u) {
                    value += rect.w;
                }
            }
            if (!found || ret.max_value < value) {
                ret = BruteResult{value, x, y, x, y, 1};
                found = true;
            } else if (ret.max_value == value) {
                ++ret.area;
                if (std::make_pair(x, y) <
                    std::make_pair(ret.minimum_x, ret.minimum_y)) {
                    ret.minimum_x = x;
                    ret.minimum_y = y;
                }
                if (std::make_pair(ret.maximum_x, ret.maximum_y) <
                    std::make_pair(x, y)) {
                    ret.maximum_x = x;
                    ret.maximum_y = y;
                }
            }
        }
    }
    assert(found);
    return ret;
}

template <class Solver>
void add_all(Solver &solver, const std::vector<TestRectangle> &rectangles) {
    for (const auto &rect : rectangles) {
        solver.add_rectangle(rect.l, rect.d, rect.r, rect.u, rect.w);
    }
}

template <class Solver>
void check_rectangle_solver(const Solver &solver,
                            const std::vector<TestRectangle> &rectangles, int l,
                            int d, int r, int u) {
    const auto expected = brute_force_rectangle(rectangles, l, d, r, u);
    const auto [min_value, min_x, min_y] =
        solver.calc_max_lexicographically_minimum_point(l, d, r, u);
    const auto [max_value, max_x, max_y] =
        solver.calc_max_lexicographically_maximum_point(l, d, r, u);
    const auto [area_value, area] =
        solver.template calc_max_area<long long>(l, d, r, u);
    assert(min_value == expected.max_value);
    assert(min_x == expected.minimum_x);
    assert(min_y == expected.minimum_y);
    assert(max_value == expected.max_value);
    assert(max_x == expected.maximum_x);
    assert(max_y == expected.maximum_y);
    assert(area_value == expected.max_value);
    assert(area == expected.area);
}

void check_rectangle(const std::vector<TestRectangle> &rectangles, int l, int d,
                     int r, int u) {
    RectangleAddMaxGet<int, long long> solver(
        static_cast<int>(rectangles.size()));
    CompressedRectangleAddMaxGet<int, long long> compressed_solver(
        static_cast<int>(rectangles.size()));
    add_all(solver, rectangles);
    add_all(compressed_solver, rectangles);
    check_rectangle_solver(solver, rectangles, l, d, r, u);
    check_rectangle_solver(compressed_solver, rectangles, l, d, r, u);

    for (int x = l; x < r; ++x) {
        for (int y = d; y < u; ++y) {
            long long value = 0;
            for (const auto &rect : rectangles) {
                if (rect.l <= x && x < rect.r && rect.d <= y && y < rect.u) {
                    value += rect.w;
                }
            }
            const auto [point_value, point_x, point_y] =
                solver.calc_max_lexicographically_minimum_point(x, y, x + 1,
                                                                y + 1);
            const auto [point_max_value, point_max_x, point_max_y] =
                compressed_solver.calc_max_lexicographically_maximum_point(
                    x, y, x + 1, y + 1);
            const auto [point_area_value, point_area] =
                solver.calc_max_area<long long>(x, y, x + 1, y + 1);
            assert(point_value == value);
            assert(point_x == x);
            assert(point_y == y);
            assert(point_max_value == value);
            assert(point_max_x == x);
            assert(point_max_y == y);
            assert(point_area_value == value);
            assert(point_area == 1);
        }
    }
}

template <class Lower, class Upper>
void check_variable(const std::vector<TestRectangle> &rectangles, int l, int r,
                    Lower lower_y, Upper upper_y) {
    RectangleAddMaxGet<int, long long> solver(
        static_cast<int>(rectangles.size()));
    add_all(solver, rectangles);
    const auto expected =
        brute_force_variable(rectangles, l, r, lower_y, upper_y);
    const auto [min_value, min_x, min_y] =
        solver.calc_max_lexicographically_minimum_point(l, r, lower_y, upper_y);
    const auto [max_value, max_x, max_y] =
        solver.calc_max_lexicographically_maximum_point(l, r, lower_y, upper_y);
    const auto [area_value, area] =
        solver.calc_max_area<long long>(l, r, lower_y, upper_y);
    assert(min_value == expected.max_value);
    assert(min_x == expected.minimum_x);
    assert(min_y == expected.minimum_y);
    assert(max_value == expected.max_value);
    assert(max_x == expected.maximum_x);
    assert(max_y == expected.maximum_y);
    assert(area_value == expected.max_value);
    assert(area == expected.area);
}

void check_no_argument(const std::vector<TestRectangle> &rectangles) {
    bool found = false;
    int l = 0, d = 0, r = 0, u = 0;
    RectangleAddMaxGet<int, long long> solver(
        static_cast<int>(rectangles.size()));
    CompressedRectangleAddMaxGet<int, long long> compressed_solver(
        static_cast<int>(rectangles.size()));
    for (const auto &rect : rectangles) {
        solver.add_rectangle(rect.l, rect.d, rect.r, rect.u, rect.w);
        compressed_solver.add_rectangle(rect.l, rect.d, rect.r, rect.u, rect.w);
        if (rect.l == rect.r || rect.d == rect.u) {
            continue;
        }
        if (!found) {
            l = rect.l;
            d = rect.d;
            r = rect.r;
            u = rect.u;
            found = true;
        } else {
            l = std::min(l, rect.l);
            d = std::min(d, rect.d);
            r = std::max(r, rect.r);
            u = std::max(u, rect.u);
        }
    }
    if (!found) {
        return;
    }
    const auto expected = brute_force_rectangle(rectangles, l, d, r, u);
    const auto [min_value, min_x, min_y] =
        solver.calc_max_lexicographically_minimum_point();
    const auto [max_value, max_x, max_y] =
        compressed_solver.calc_max_lexicographically_maximum_point();
    const auto [area_value, area] = solver.calc_max_area<long long>();
    assert(min_value == expected.max_value);
    assert(min_x == expected.minimum_x);
    assert(min_y == expected.minimum_y);
    assert(max_value == expected.max_value);
    assert(max_x == expected.maximum_x);
    assert(max_y == expected.maximum_y);
    assert(area_value == expected.max_value);
    assert(area == expected.area);
}

void self_test() {
    const long long max_weight = std::numeric_limits<long long>::max();
    const std::vector<std::vector<TestRectangle>> cases = {
        {},
        {{0, 0, 2, 2, 1}},
        {{0, 0, 3, 3, -1}},
        {{0, 0, 3, 2, 2}, {1, 1, 4, 4, 3}},
        {{-1, -1, 2, 1, 5}, {0, -2, 3, 2, -2}, {1, 0, 2, 3, 4}},
        {{0, 0, 2, 2, 1}, {0, 0, 2, 2, 1}, {1, 1, 3, 3, -3}},
        {{0, 0, 1, 1, max_weight}, {1, 0, 2, 1, max_weight}},
        {{0, 0, 2, 2, 3},
         {0, 0, 0, 2, std::numeric_limits<long long>::lowest()},
         {1, 1, 2, 1, std::numeric_limits<long long>::lowest()}},
    };
    const std::vector<std::tuple<int, int, int, int>> queries = {
        {-2, -2, 4, 4}, {0, 0, 3, 3}, {1, 1, 4, 5},
        {-1, 0, 2, 2},  {2, 2, 5, 5}, {0, 0, 2, 1},
    };
    for (const auto &rectangles : cases) {
        for (const auto &[l, d, r, u] : queries) {
            check_rectangle(rectangles, l, d, r, u);
        }
        check_no_argument(rectangles);
    }

    for (const auto &rectangles : cases) {
        check_variable(
            rectangles, -2, 5, [](int x) { return x <= 0 ? -2 : x - 2; },
            [](int x) { return x <= 1 ? 2 : x + 1; });
        check_variable(
            rectangles, -1, 4, [](int x) { return x == 1 ? 0 : -1; },
            [](int x) { return x == 1 ? 0 : 3; });
    }

    {
        CompressedRectangleAddMaxGet<int, long long> solver;
        solver.add_rectangle(-1, -1, 1, 1, -5);
        const int l = std::numeric_limits<int>::lowest();
        const int d = std::numeric_limits<int>::lowest();
        const int r = std::numeric_limits<int>::max();
        const int u = std::numeric_limits<int>::max();
        const auto [min_value, min_x, min_y] =
            solver.calc_max_lexicographically_minimum_point(l, d, r, u);
        const auto [max_value, max_x, max_y] =
            solver.calc_max_lexicographically_maximum_point(l, d, r, u);
        assert(min_value == 0);
        assert(min_x == l);
        assert(min_y == d);
        assert(max_value == 0);
        assert(max_x == r - 1);
        assert(max_y == u - 1);
    }
    {
        RectangleAddMaxGet<int, long long> solver;
        CompressedRectangleAddMaxGet<int, long long> compressed_solver;
        solver.add_rectangle(0, 0, 0, 5,
                             std::numeric_limits<long long>::lowest());
        solver.add_rectangle(2, 2, 7, 2,
                             std::numeric_limits<long long>::lowest());
        compressed_solver.add_rectangle(
            0, 0, 0, 5, std::numeric_limits<long long>::lowest());
        assert(solver.rectangles.empty());
        assert(compressed_solver.rectangles.empty());

        const auto [value, x, y] =
            solver.calc_max_lexicographically_minimum_point();
        const auto [area_value, area] = solver.calc_max_area<long long>();
        assert(value == 0);
        assert(x == 0);
        assert(y == 0);
        assert(area_value == 0);
        assert(area == 0);
    }
}
} // namespace

int main() {
    self_test();
    return 0;
}
#line 1 "verify/standalone-rectangle-add-max-get.test.cpp"
// competitive-verifier: STANDALONE

#include <algorithm>
#include <cassert>
#include <limits>
#include <tuple>
#include <utility>
#include <vector>

#line 1 "other/rectangle-add-max-get.hpp"



// 2 次元平面上の重み付き半開長方形を加算し、最大値を求める。
// RectangleAddMaxGet は座標圧縮せず、x 座標を 1 ずつ走査する。
// CompressedRectangleAddMaxGet は座標圧縮して平面走査する。
// 面積 0 の長方形は無視する。
// 重みは負でもよいが、正の面積で符号付き整数型の最小値は使わない。
// 計算量は各 calc の説明を参照する。

#line 15 "other/rectangle-add-max-get.hpp"
#include <type_traits>
#line 18 "other/rectangle-add-max-get.hpp"

namespace rectangle_add_max_get_internal {
template <class T> using CoordinateLength = std::make_unsigned_t<T>;

template <class T, class C> struct Rectangle {
    T l, d, r, u;
    C w;
};

template <class C> void assert_valid_weight(C w) {
    if constexpr (std::numeric_limits<C>::is_specialized &&
                  std::numeric_limits<C>::is_integer &&
                  std::numeric_limits<C>::is_signed) {
        assert(w != std::numeric_limits<C>::lowest());
    }
}

template <class T> CoordinateLength<T> coordinate_difference(T l, T r) {
    assert(l <= r);
    return static_cast<CoordinateLength<T>>(r) -
           static_cast<CoordinateLength<T>>(l);
}

template <class Length> int checked_size(Length n) {
    assert(n <= static_cast<Length>(std::numeric_limits<int>::max()));
    return static_cast<int>(n);
}

template <class T> T add_length(T x, int n) {
    return static_cast<T>(x + static_cast<T>(n));
}

template <class T, class C> struct SegmentTree {
    using Length = CoordinateLength<T>;

    struct Node {
        C max_value;
        Length length;
        T minimum_y;
        T maximum_y;
    };

    int size = 1;
    std::vector<Node> data;
    std::vector<C> lazy;

    SegmentTree() = default;

    explicit SegmentTree(const std::vector<Node> &leaves) {
        const int n = static_cast<int>(leaves.size());
        assert(n > 0);
        while (size < n) {
            size <<= 1;
        }
        data.assign(size << 1, Node{C(), Length(), T(), T()});
        lazy.assign(size << 1, C());
        for (int i = 0; i < n; ++i) {
            data[size + i] = leaves[i];
        }
        for (int i = size - 1; i >= 1; --i) {
            data[i] = merge(data[i << 1], data[i << 1 | 1]);
        }
    }

    static Node merge(const Node &a, const Node &b) {
        if (a.length == Length()) {
            return b;
        }
        if (b.length == Length()) {
            return a;
        }
        if (a.max_value > b.max_value) {
            return a;
        }
        if (a.max_value < b.max_value) {
            return b;
        }
        return Node{a.max_value, a.length + b.length, a.minimum_y, b.maximum_y};
    }

    void apply(int l, int r, C w, bool add) { apply(1, 0, size, l, r, w, add); }

    const Node &all_prod() const { return data[1]; }

    Node prod(int l, int r) { return prod(1, 0, size, l, r); }

    void add_node(int v, C w, bool add) {
        if (add) {
            data[v].max_value += w;
            lazy[v] += w;
        } else {
            data[v].max_value -= w;
            lazy[v] -= w;
        }
    }

    void push(int v) {
        if (lazy[v] == C()) {
            return;
        }
        add_node(v << 1, lazy[v], true);
        add_node(v << 1 | 1, lazy[v], true);
        lazy[v] = C();
    }

    void apply(int v, int l, int r, int ql, int qr, C w, bool add) {
        if (qr <= l || r <= ql) {
            return;
        }
        if (ql <= l && r <= qr) {
            add_node(v, w, add);
            return;
        }
        push(v);
        const int m = (l + r) >> 1;
        apply(v << 1, l, m, ql, qr, w, add);
        apply(v << 1 | 1, m, r, ql, qr, w, add);
        data[v] = merge(data[v << 1], data[v << 1 | 1]);
    }

    Node prod(int v, int l, int r, int ql, int qr) {
        if (qr <= l || r <= ql) {
            return Node{C(), Length(), T(), T()};
        }
        if (ql <= l && r <= qr) {
            return data[v];
        }
        push(v);
        const int m = (l + r) >> 1;
        return merge(prod(v << 1, l, m, ql, qr),
                     prod(v << 1 | 1, m, r, ql, qr));
    }
};
} // namespace rectangle_add_max_get_internal

template <class T, class C> struct CompressedRectangleAddMaxGet {
    static_assert(std::is_integral_v<T> && !std::is_same_v<T, bool>,
                  "T must be integer.");

    using Rectangle = rectangle_add_max_get_internal::Rectangle<T, C>;

    std::vector<Rectangle> rectangles;

    CompressedRectangleAddMaxGet() = default;

    explicit CompressedRectangleAddMaxGet(int n) {
        assert(n >= 0);
        rectangles.reserve(n);
    }

    void add_rectangle(T l, T d, T r, T u, C w = C(1)) {
        assert(l <= r);
        assert(d <= u);
        if (l == r || d == u) {
            return;
        }
        rectangle_add_max_get_internal::assert_valid_weight(w);
        rectangles.emplace_back(Rectangle{l, d, r, u, w});
    }

    std::tuple<C, T, T> calc_max_lexicographically_minimum_point() const {
        const auto query = make_bounding_query_range();
        if (query.empty) {
            return {C(), T(), T()};
        }
        const auto result = calc_impl<T, false>(query);
        return {result.max_value, result.minimum_x, result.minimum_y};
    }

    std::tuple<C, T, T> calc_max_lexicographically_minimum_point(T l, T d, T r,
                                                                 T u) const {
        const auto query = make_query_range(l, d, r, u);
        const auto result = calc_impl<T, false>(query);
        return {result.max_value, result.minimum_x, result.minimum_y};
    }

    std::tuple<C, T, T> calc_max_lexicographically_maximum_point() const {
        const auto query = make_bounding_query_range();
        if (query.empty) {
            return {C(), T(), T()};
        }
        const auto result = calc_impl<T, false>(query);
        return {result.max_value, result.maximum_x, result.maximum_y};
    }

    std::tuple<C, T, T> calc_max_lexicographically_maximum_point(T l, T d, T r,
                                                                 T u) const {
        const auto query = make_query_range(l, d, r, u);
        const auto result = calc_impl<T, false>(query);
        return {result.max_value, result.maximum_x, result.maximum_y};
    }

    template <class T2 = T> std::pair<C, T2> calc_max_area() const {
        const auto query = make_bounding_query_range();
        if (query.empty) {
            return {C(), T2()};
        }
        const auto result = calc_impl<T2, true>(query);
        return {result.max_value, result.max_area};
    }

    template <class T2 = T>
    std::pair<C, T2> calc_max_area(T l, T d, T r, T u) const {
        const auto query = make_query_range(l, d, r, u);
        const auto result = calc_impl<T2, true>(query);
        return {result.max_value, result.max_area};
    }

  private:
    using SegmentTree = rectangle_add_max_get_internal::SegmentTree<T, C>;
    using Node = typename SegmentTree::Node;

    struct QueryRange {
        T l, d, r, u;
        bool empty;
    };

    struct Event {
        T x;
        int d, u;
        C w;
        bool add;
    };

    template <class T2> struct Result {
        C max_value{};
        T minimum_x{};
        T minimum_y{};
        T maximum_x{};
        T maximum_y{};
        T2 max_area{};
    };

    QueryRange make_bounding_query_range() const {
        if (rectangles.empty()) {
            return QueryRange{T(), T(), T(), T(), true};
        }
        T l = rectangles[0].l;
        T d = rectangles[0].d;
        T r = rectangles[0].r;
        T u = rectangles[0].u;
        for (const auto &rect : rectangles) {
            l = std::min(l, rect.l);
            d = std::min(d, rect.d);
            r = std::max(r, rect.r);
            u = std::max(u, rect.u);
        }
        return QueryRange{l, d, r, u, false};
    }

    QueryRange make_query_range(T l, T d, T r, T u) const {
        assert(l < r);
        assert(d < u);
        return QueryRange{l, d, r, u, false};
    }

    template <class T2, bool NeedArea>
    Result<T2> calc_impl(const QueryRange &query) const {
        std::vector<T> xs{query.l, query.r};
        std::vector<T> ys{query.d, query.u};
        std::vector<Rectangle> clipped;
        clipped.reserve(rectangles.size());
        for (const auto &rect : rectangles) {
            const T l = std::max(rect.l, query.l);
            const T d = std::max(rect.d, query.d);
            const T r = std::min(rect.r, query.r);
            const T u = std::min(rect.u, query.u);
            if (l >= r || d >= u) {
                continue;
            }
            clipped.emplace_back(Rectangle{l, d, r, u, rect.w});
            xs.emplace_back(l);
            xs.emplace_back(r);
            ys.emplace_back(d);
            ys.emplace_back(u);
        }
        std::sort(xs.begin(), xs.end());
        xs.erase(std::unique(xs.begin(), xs.end()), xs.end());
        std::sort(ys.begin(), ys.end());
        ys.erase(std::unique(ys.begin(), ys.end()), ys.end());

        std::vector<Event> events;
        events.reserve(clipped.size() * 2);
        for (const auto &rect : clipped) {
            const int d = static_cast<int>(
                std::lower_bound(ys.begin(), ys.end(), rect.d) - ys.begin());
            const int u = static_cast<int>(
                std::lower_bound(ys.begin(), ys.end(), rect.u) - ys.begin());
            events.emplace_back(Event{rect.l, d, u, rect.w, true});
            events.emplace_back(Event{rect.r, d, u, rect.w, false});
        }
        std::sort(events.begin(), events.end(),
                  [](const Event &a, const Event &b) {
                      if (a.x != b.x) {
                          return a.x < b.x;
                      }
                      return a.add < b.add;
                  });

        std::vector<Node> leaves;
        leaves.reserve(ys.size() - 1);
        for (int i = 0; i + 1 < static_cast<int>(ys.size()); ++i) {
            leaves.emplace_back(
                Node{C(),
                     rectangle_add_max_get_internal::coordinate_difference(
                         ys[i], ys[i + 1]),
                     ys[i], static_cast<T>(ys[i + 1] - T(1))});
        }
        SegmentTree seg(leaves);

        Result<T2> ret;
        bool found = false;
        int event_index = 0;
        for (int i = 0; i + 1 < static_cast<int>(xs.size()); ++i) {
            while (event_index < static_cast<int>(events.size()) &&
                   events[event_index].x == xs[i]) {
                seg.apply(events[event_index].d, events[event_index].u,
                          events[event_index].w, events[event_index].add);
                ++event_index;
            }
            const Node &now = seg.all_prod();
            const T minimum_x = xs[i];
            const T maximum_x = static_cast<T>(xs[i + 1] - T(1));
            T2 area = T2();
            if constexpr (NeedArea) {
                const auto dx =
                    rectangle_add_max_get_internal::coordinate_difference(
                        xs[i], xs[i + 1]);
                area = static_cast<T2>(dx) * static_cast<T2>(now.length);
            }
            if (!found || ret.max_value < now.max_value) {
                ret.max_value = now.max_value;
                ret.minimum_x = minimum_x;
                ret.minimum_y = now.minimum_y;
                ret.maximum_x = maximum_x;
                ret.maximum_y = now.maximum_y;
                if constexpr (NeedArea) {
                    ret.max_area = area;
                }
                found = true;
            } else if (ret.max_value == now.max_value) {
                if (std::make_pair(minimum_x, now.minimum_y) <
                    std::make_pair(ret.minimum_x, ret.minimum_y)) {
                    ret.minimum_x = minimum_x;
                    ret.minimum_y = now.minimum_y;
                }
                if (std::make_pair(ret.maximum_x, ret.maximum_y) <
                    std::make_pair(maximum_x, now.maximum_y)) {
                    ret.maximum_x = maximum_x;
                    ret.maximum_y = now.maximum_y;
                }
                if constexpr (NeedArea) {
                    ret.max_area += area;
                }
            }
        }
        return ret;
    }
};

template <class T, class C> struct RectangleAddMaxGet {
    static_assert(std::is_integral_v<T> && !std::is_same_v<T, bool>,
                  "T must be integer.");

    using Rectangle = rectangle_add_max_get_internal::Rectangle<T, C>;

    std::vector<Rectangle> rectangles;

    RectangleAddMaxGet() = default;

    explicit RectangleAddMaxGet(int n) {
        assert(n >= 0);
        rectangles.reserve(n);
    }

    void add_rectangle(T l, T d, T r, T u, C w = C(1)) {
        assert(l <= r);
        assert(d <= u);
        if (l == r || d == u) {
            return;
        }
        rectangle_add_max_get_internal::assert_valid_weight(w);
        rectangles.emplace_back(Rectangle{l, d, r, u, w});
    }

    std::tuple<C, T, T> calc_max_lexicographically_minimum_point() const {
        const auto query = make_bounding_query_range();
        if (query.empty) {
            return {C(), T(), T()};
        }
        const auto result = calc_rectangle_impl<T, false>(query);
        return {result.max_value, result.minimum_x, result.minimum_y};
    }

    std::tuple<C, T, T> calc_max_lexicographically_minimum_point(T l, T d, T r,
                                                                 T u) const {
        const auto query = make_query_range(l, d, r, u);
        const auto result = calc_rectangle_impl<T, false>(query);
        return {result.max_value, result.minimum_x, result.minimum_y};
    }

    template <class Lower, class Upper>
    std::tuple<C, T, T>
    calc_max_lexicographically_minimum_point(T l, T r, Lower lower_y,
                                             Upper upper_y) const {
        const auto query = make_variable_query_range(l, r, lower_y, upper_y);
        const auto result = calc_variable_impl<T, false>(query);
        return {result.max_value, result.minimum_x, result.minimum_y};
    }

    std::tuple<C, T, T> calc_max_lexicographically_maximum_point() const {
        const auto query = make_bounding_query_range();
        if (query.empty) {
            return {C(), T(), T()};
        }
        const auto result = calc_rectangle_impl<T, false>(query);
        return {result.max_value, result.maximum_x, result.maximum_y};
    }

    std::tuple<C, T, T> calc_max_lexicographically_maximum_point(T l, T d, T r,
                                                                 T u) const {
        const auto query = make_query_range(l, d, r, u);
        const auto result = calc_rectangle_impl<T, false>(query);
        return {result.max_value, result.maximum_x, result.maximum_y};
    }

    template <class Lower, class Upper>
    std::tuple<C, T, T>
    calc_max_lexicographically_maximum_point(T l, T r, Lower lower_y,
                                             Upper upper_y) const {
        const auto query = make_variable_query_range(l, r, lower_y, upper_y);
        const auto result = calc_variable_impl<T, false>(query);
        return {result.max_value, result.maximum_x, result.maximum_y};
    }

    template <class T2 = T> std::pair<C, T2> calc_max_area() const {
        const auto query = make_bounding_query_range();
        if (query.empty) {
            return {C(), T2()};
        }
        const auto result = calc_rectangle_impl<T2, true>(query);
        return {result.max_value, result.max_area};
    }

    template <class T2 = T>
    std::pair<C, T2> calc_max_area(T l, T d, T r, T u) const {
        const auto query = make_query_range(l, d, r, u);
        const auto result = calc_rectangle_impl<T2, true>(query);
        return {result.max_value, result.max_area};
    }

    template <class T2 = T, class Lower, class Upper>
    std::pair<C, T2> calc_max_area(T l, T r, Lower lower_y,
                                   Upper upper_y) const {
        const auto query = make_variable_query_range(l, r, lower_y, upper_y);
        const auto result = calc_variable_impl<T2, true>(query);
        return {result.max_value, result.max_area};
    }

  private:
    using Length = rectangle_add_max_get_internal::CoordinateLength<T>;
    using SegmentTree = rectangle_add_max_get_internal::SegmentTree<T, C>;
    using Node = typename SegmentTree::Node;

    struct QueryRange {
        T l, d, r, u;
        bool empty;
    };

    struct VariableQueryRange {
        T l, r, d, u;
        std::vector<T> lower_y, upper_y;
    };

    struct Event {
        int next;
        int d, u;
        C w;
    };

    struct EventList {
        std::vector<int> add_head, remove_head;
        std::vector<Event> events;
    };

    template <class T2> struct Result {
        C max_value{};
        T minimum_x{};
        T minimum_y{};
        T maximum_x{};
        T maximum_y{};
        T2 max_area{};
    };

    QueryRange make_bounding_query_range() const {
        if (rectangles.empty()) {
            return QueryRange{T(), T(), T(), T(), true};
        }
        T l = rectangles[0].l;
        T d = rectangles[0].d;
        T r = rectangles[0].r;
        T u = rectangles[0].u;
        for (const auto &rect : rectangles) {
            l = std::min(l, rect.l);
            d = std::min(d, rect.d);
            r = std::max(r, rect.r);
            u = std::max(u, rect.u);
        }
        return QueryRange{l, d, r, u, false};
    }

    QueryRange make_query_range(T l, T d, T r, T u) const {
        assert(l < r);
        assert(d < u);
        return QueryRange{l, d, r, u, false};
    }

    template <class Lower, class Upper>
    VariableQueryRange make_variable_query_range(T l, T r, Lower lower_y,
                                                 Upper upper_y) const {
        assert(l < r);
        const int x_count = rectangle_add_max_get_internal::checked_size(
            rectangle_add_max_get_internal::coordinate_difference(l, r));
        std::vector<T> lower_values(x_count), upper_values(x_count);
        bool found = false;
        T d = T(), u = T();
        for (int i = 0; i < x_count; ++i) {
            const T x = rectangle_add_max_get_internal::add_length(l, i);
            const T lower = static_cast<T>(lower_y(x));
            const T upper = static_cast<T>(upper_y(x));
            assert(lower <= upper);
            lower_values[i] = lower;
            upper_values[i] = upper;
            if (lower == upper) {
                continue;
            }
            if (!found) {
                d = lower;
                u = upper;
                found = true;
            } else {
                d = std::min(d, lower);
                u = std::max(u, upper);
            }
        }
        assert(found);
        return VariableQueryRange{
            l, r, d, u, std::move(lower_values), std::move(upper_values)};
    }

    static void add_event(std::vector<int> &head, std::vector<Event> &events,
                          int x, int d, int u, C w) {
        events.emplace_back(Event{head[x], d, u, w});
        head[x] = static_cast<int>(events.size()) - 1;
    }

    EventList make_event_list(T l, T d, T r, T u, int x_count) const {
        EventList event_list;
        event_list.add_head.assign(x_count, -1);
        event_list.remove_head.assign(x_count, -1);
        event_list.events.reserve(rectangles.size() * 2);
        for (const auto &rect : rectangles) {
            const T cl = std::max(rect.l, l);
            const T cd = std::max(rect.d, d);
            const T cr = std::min(rect.r, r);
            const T cu = std::min(rect.u, u);
            if (cl >= cr || cd >= cu) {
                continue;
            }
            const int xl = rectangle_add_max_get_internal::checked_size(
                rectangle_add_max_get_internal::coordinate_difference(l, cl));
            const int xr = rectangle_add_max_get_internal::checked_size(
                rectangle_add_max_get_internal::coordinate_difference(l, cr));
            const int yd = rectangle_add_max_get_internal::checked_size(
                rectangle_add_max_get_internal::coordinate_difference(d, cd));
            const int yu = rectangle_add_max_get_internal::checked_size(
                rectangle_add_max_get_internal::coordinate_difference(d, cu));
            add_event(event_list.add_head, event_list.events, xl, yd, yu,
                      rect.w);
            if (xr < x_count) {
                add_event(event_list.remove_head, event_list.events, xr, yd, yu,
                          rect.w);
            }
        }
        return event_list;
    }

    static std::vector<Node> make_unit_leaves(T d, int y_count) {
        std::vector<Node> leaves;
        leaves.reserve(y_count);
        for (int i = 0; i < y_count; ++i) {
            const T y = rectangle_add_max_get_internal::add_length(d, i);
            leaves.emplace_back(Node{C(), Length(1), y, y});
        }
        return leaves;
    }

    static void apply_events(SegmentTree &seg, const EventList &event_list,
                             int x) {
        for (int i = event_list.remove_head[x]; i != -1;
             i = event_list.events[i].next) {
            seg.apply(event_list.events[i].d, event_list.events[i].u,
                      event_list.events[i].w, false);
        }
        for (int i = event_list.add_head[x]; i != -1;
             i = event_list.events[i].next) {
            seg.apply(event_list.events[i].d, event_list.events[i].u,
                      event_list.events[i].w, true);
        }
    }

    template <class T2, bool NeedArea>
    static void update_result(Result<T2> &ret, bool &found, C value,
                              T minimum_x, T minimum_y, T maximum_x,
                              T maximum_y, T2 area) {
        if (!found || ret.max_value < value) {
            ret.max_value = value;
            ret.minimum_x = minimum_x;
            ret.minimum_y = minimum_y;
            ret.maximum_x = maximum_x;
            ret.maximum_y = maximum_y;
            if constexpr (NeedArea) {
                ret.max_area = area;
            }
            found = true;
        } else if (ret.max_value == value) {
            if (std::make_pair(minimum_x, minimum_y) <
                std::make_pair(ret.minimum_x, ret.minimum_y)) {
                ret.minimum_x = minimum_x;
                ret.minimum_y = minimum_y;
            }
            if (std::make_pair(ret.maximum_x, ret.maximum_y) <
                std::make_pair(maximum_x, maximum_y)) {
                ret.maximum_x = maximum_x;
                ret.maximum_y = maximum_y;
            }
            if constexpr (NeedArea) {
                ret.max_area += area;
            }
        }
    }

    template <class T2, bool NeedArea>
    Result<T2> calc_rectangle_impl(const QueryRange &query) const {
        const int x_count = rectangle_add_max_get_internal::checked_size(
            rectangle_add_max_get_internal::coordinate_difference(query.l,
                                                                  query.r));
        const int y_count = rectangle_add_max_get_internal::checked_size(
            rectangle_add_max_get_internal::coordinate_difference(query.d,
                                                                  query.u));
        SegmentTree seg(make_unit_leaves(query.d, y_count));
        const EventList event_list =
            make_event_list(query.l, query.d, query.r, query.u, x_count);

        Result<T2> ret;
        bool found = false;
        for (int i = 0; i < x_count; ++i) {
            apply_events(seg, event_list, i);
            const Node &now = seg.all_prod();
            const T x = rectangle_add_max_get_internal::add_length(query.l, i);
            T2 area = T2();
            if constexpr (NeedArea) {
                area = static_cast<T2>(now.length);
            }
            update_result<T2, NeedArea>(ret, found, now.max_value, x,
                                        now.minimum_y, x, now.maximum_y, area);
        }
        assert(found);
        return ret;
    }

    template <class T2, bool NeedArea>
    Result<T2> calc_variable_impl(const VariableQueryRange &query) const {
        const int x_count = static_cast<int>(query.lower_y.size());
        const int y_count = rectangle_add_max_get_internal::checked_size(
            rectangle_add_max_get_internal::coordinate_difference(query.d,
                                                                  query.u));
        SegmentTree seg(make_unit_leaves(query.d, y_count));
        const EventList event_list =
            make_event_list(query.l, query.d, query.r, query.u, x_count);

        Result<T2> ret;
        bool found = false;
        for (int i = 0; i < x_count; ++i) {
            apply_events(seg, event_list, i);
            if (query.lower_y[i] == query.upper_y[i]) {
                continue;
            }
            const int d = rectangle_add_max_get_internal::checked_size(
                rectangle_add_max_get_internal::coordinate_difference(
                    query.d, query.lower_y[i]));
            const int u = rectangle_add_max_get_internal::checked_size(
                rectangle_add_max_get_internal::coordinate_difference(
                    query.d, query.upper_y[i]));
            const Node now = seg.prod(d, u);
            const T x = rectangle_add_max_get_internal::add_length(query.l, i);
            T2 area = T2();
            if constexpr (NeedArea) {
                area = static_cast<T2>(now.length);
            }
            update_result<T2, NeedArea>(ret, found, now.max_value, x,
                                        now.minimum_y, x, now.maximum_y, area);
        }
        assert(found);
        return ret;
    }
};


#line 11 "verify/standalone-rectangle-add-max-get.test.cpp"

namespace {
struct TestRectangle {
    int l, d, r, u;
    long long w;
};

struct BruteResult {
    long long max_value;
    int minimum_x;
    int minimum_y;
    int maximum_x;
    int maximum_y;
    long long area;
};

BruteResult brute_force_rectangle(const std::vector<TestRectangle> &rectangles,
                                  int l, int d, int r, int u) {
    bool found = false;
    BruteResult ret{};
    for (int x = l; x < r; ++x) {
        for (int y = d; y < u; ++y) {
            long long value = 0;
            for (const auto &rect : rectangles) {
                if (rect.l <= x && x < rect.r && rect.d <= y && y < rect.u) {
                    value += rect.w;
                }
            }
            if (!found || ret.max_value < value) {
                ret = BruteResult{value, x, y, x, y, 1};
                found = true;
            } else if (ret.max_value == value) {
                ++ret.area;
                if (std::make_pair(x, y) <
                    std::make_pair(ret.minimum_x, ret.minimum_y)) {
                    ret.minimum_x = x;
                    ret.minimum_y = y;
                }
                if (std::make_pair(ret.maximum_x, ret.maximum_y) <
                    std::make_pair(x, y)) {
                    ret.maximum_x = x;
                    ret.maximum_y = y;
                }
            }
        }
    }
    assert(found);
    return ret;
}

template <class Lower, class Upper>
BruteResult brute_force_variable(const std::vector<TestRectangle> &rectangles,
                                 int l, int r, Lower lower_y, Upper upper_y) {
    bool found = false;
    BruteResult ret{};
    for (int x = l; x < r; ++x) {
        const int d = lower_y(x);
        const int u = upper_y(x);
        assert(d <= u);
        for (int y = d; y < u; ++y) {
            long long value = 0;
            for (const auto &rect : rectangles) {
                if (rect.l <= x && x < rect.r && rect.d <= y && y < rect.u) {
                    value += rect.w;
                }
            }
            if (!found || ret.max_value < value) {
                ret = BruteResult{value, x, y, x, y, 1};
                found = true;
            } else if (ret.max_value == value) {
                ++ret.area;
                if (std::make_pair(x, y) <
                    std::make_pair(ret.minimum_x, ret.minimum_y)) {
                    ret.minimum_x = x;
                    ret.minimum_y = y;
                }
                if (std::make_pair(ret.maximum_x, ret.maximum_y) <
                    std::make_pair(x, y)) {
                    ret.maximum_x = x;
                    ret.maximum_y = y;
                }
            }
        }
    }
    assert(found);
    return ret;
}

template <class Solver>
void add_all(Solver &solver, const std::vector<TestRectangle> &rectangles) {
    for (const auto &rect : rectangles) {
        solver.add_rectangle(rect.l, rect.d, rect.r, rect.u, rect.w);
    }
}

template <class Solver>
void check_rectangle_solver(const Solver &solver,
                            const std::vector<TestRectangle> &rectangles, int l,
                            int d, int r, int u) {
    const auto expected = brute_force_rectangle(rectangles, l, d, r, u);
    const auto [min_value, min_x, min_y] =
        solver.calc_max_lexicographically_minimum_point(l, d, r, u);
    const auto [max_value, max_x, max_y] =
        solver.calc_max_lexicographically_maximum_point(l, d, r, u);
    const auto [area_value, area] =
        solver.template calc_max_area<long long>(l, d, r, u);
    assert(min_value == expected.max_value);
    assert(min_x == expected.minimum_x);
    assert(min_y == expected.minimum_y);
    assert(max_value == expected.max_value);
    assert(max_x == expected.maximum_x);
    assert(max_y == expected.maximum_y);
    assert(area_value == expected.max_value);
    assert(area == expected.area);
}

void check_rectangle(const std::vector<TestRectangle> &rectangles, int l, int d,
                     int r, int u) {
    RectangleAddMaxGet<int, long long> solver(
        static_cast<int>(rectangles.size()));
    CompressedRectangleAddMaxGet<int, long long> compressed_solver(
        static_cast<int>(rectangles.size()));
    add_all(solver, rectangles);
    add_all(compressed_solver, rectangles);
    check_rectangle_solver(solver, rectangles, l, d, r, u);
    check_rectangle_solver(compressed_solver, rectangles, l, d, r, u);

    for (int x = l; x < r; ++x) {
        for (int y = d; y < u; ++y) {
            long long value = 0;
            for (const auto &rect : rectangles) {
                if (rect.l <= x && x < rect.r && rect.d <= y && y < rect.u) {
                    value += rect.w;
                }
            }
            const auto [point_value, point_x, point_y] =
                solver.calc_max_lexicographically_minimum_point(x, y, x + 1,
                                                                y + 1);
            const auto [point_max_value, point_max_x, point_max_y] =
                compressed_solver.calc_max_lexicographically_maximum_point(
                    x, y, x + 1, y + 1);
            const auto [point_area_value, point_area] =
                solver.calc_max_area<long long>(x, y, x + 1, y + 1);
            assert(point_value == value);
            assert(point_x == x);
            assert(point_y == y);
            assert(point_max_value == value);
            assert(point_max_x == x);
            assert(point_max_y == y);
            assert(point_area_value == value);
            assert(point_area == 1);
        }
    }
}

template <class Lower, class Upper>
void check_variable(const std::vector<TestRectangle> &rectangles, int l, int r,
                    Lower lower_y, Upper upper_y) {
    RectangleAddMaxGet<int, long long> solver(
        static_cast<int>(rectangles.size()));
    add_all(solver, rectangles);
    const auto expected =
        brute_force_variable(rectangles, l, r, lower_y, upper_y);
    const auto [min_value, min_x, min_y] =
        solver.calc_max_lexicographically_minimum_point(l, r, lower_y, upper_y);
    const auto [max_value, max_x, max_y] =
        solver.calc_max_lexicographically_maximum_point(l, r, lower_y, upper_y);
    const auto [area_value, area] =
        solver.calc_max_area<long long>(l, r, lower_y, upper_y);
    assert(min_value == expected.max_value);
    assert(min_x == expected.minimum_x);
    assert(min_y == expected.minimum_y);
    assert(max_value == expected.max_value);
    assert(max_x == expected.maximum_x);
    assert(max_y == expected.maximum_y);
    assert(area_value == expected.max_value);
    assert(area == expected.area);
}

void check_no_argument(const std::vector<TestRectangle> &rectangles) {
    bool found = false;
    int l = 0, d = 0, r = 0, u = 0;
    RectangleAddMaxGet<int, long long> solver(
        static_cast<int>(rectangles.size()));
    CompressedRectangleAddMaxGet<int, long long> compressed_solver(
        static_cast<int>(rectangles.size()));
    for (const auto &rect : rectangles) {
        solver.add_rectangle(rect.l, rect.d, rect.r, rect.u, rect.w);
        compressed_solver.add_rectangle(rect.l, rect.d, rect.r, rect.u, rect.w);
        if (rect.l == rect.r || rect.d == rect.u) {
            continue;
        }
        if (!found) {
            l = rect.l;
            d = rect.d;
            r = rect.r;
            u = rect.u;
            found = true;
        } else {
            l = std::min(l, rect.l);
            d = std::min(d, rect.d);
            r = std::max(r, rect.r);
            u = std::max(u, rect.u);
        }
    }
    if (!found) {
        return;
    }
    const auto expected = brute_force_rectangle(rectangles, l, d, r, u);
    const auto [min_value, min_x, min_y] =
        solver.calc_max_lexicographically_minimum_point();
    const auto [max_value, max_x, max_y] =
        compressed_solver.calc_max_lexicographically_maximum_point();
    const auto [area_value, area] = solver.calc_max_area<long long>();
    assert(min_value == expected.max_value);
    assert(min_x == expected.minimum_x);
    assert(min_y == expected.minimum_y);
    assert(max_value == expected.max_value);
    assert(max_x == expected.maximum_x);
    assert(max_y == expected.maximum_y);
    assert(area_value == expected.max_value);
    assert(area == expected.area);
}

void self_test() {
    const long long max_weight = std::numeric_limits<long long>::max();
    const std::vector<std::vector<TestRectangle>> cases = {
        {},
        {{0, 0, 2, 2, 1}},
        {{0, 0, 3, 3, -1}},
        {{0, 0, 3, 2, 2}, {1, 1, 4, 4, 3}},
        {{-1, -1, 2, 1, 5}, {0, -2, 3, 2, -2}, {1, 0, 2, 3, 4}},
        {{0, 0, 2, 2, 1}, {0, 0, 2, 2, 1}, {1, 1, 3, 3, -3}},
        {{0, 0, 1, 1, max_weight}, {1, 0, 2, 1, max_weight}},
        {{0, 0, 2, 2, 3},
         {0, 0, 0, 2, std::numeric_limits<long long>::lowest()},
         {1, 1, 2, 1, std::numeric_limits<long long>::lowest()}},
    };
    const std::vector<std::tuple<int, int, int, int>> queries = {
        {-2, -2, 4, 4}, {0, 0, 3, 3}, {1, 1, 4, 5},
        {-1, 0, 2, 2},  {2, 2, 5, 5}, {0, 0, 2, 1},
    };
    for (const auto &rectangles : cases) {
        for (const auto &[l, d, r, u] : queries) {
            check_rectangle(rectangles, l, d, r, u);
        }
        check_no_argument(rectangles);
    }

    for (const auto &rectangles : cases) {
        check_variable(
            rectangles, -2, 5, [](int x) { return x <= 0 ? -2 : x - 2; },
            [](int x) { return x <= 1 ? 2 : x + 1; });
        check_variable(
            rectangles, -1, 4, [](int x) { return x == 1 ? 0 : -1; },
            [](int x) { return x == 1 ? 0 : 3; });
    }

    {
        CompressedRectangleAddMaxGet<int, long long> solver;
        solver.add_rectangle(-1, -1, 1, 1, -5);
        const int l = std::numeric_limits<int>::lowest();
        const int d = std::numeric_limits<int>::lowest();
        const int r = std::numeric_limits<int>::max();
        const int u = std::numeric_limits<int>::max();
        const auto [min_value, min_x, min_y] =
            solver.calc_max_lexicographically_minimum_point(l, d, r, u);
        const auto [max_value, max_x, max_y] =
            solver.calc_max_lexicographically_maximum_point(l, d, r, u);
        assert(min_value == 0);
        assert(min_x == l);
        assert(min_y == d);
        assert(max_value == 0);
        assert(max_x == r - 1);
        assert(max_y == u - 1);
    }
    {
        RectangleAddMaxGet<int, long long> solver;
        CompressedRectangleAddMaxGet<int, long long> compressed_solver;
        solver.add_rectangle(0, 0, 0, 5,
                             std::numeric_limits<long long>::lowest());
        solver.add_rectangle(2, 2, 7, 2,
                             std::numeric_limits<long long>::lowest());
        compressed_solver.add_rectangle(
            0, 0, 0, 5, std::numeric_limits<long long>::lowest());
        assert(solver.rectangles.empty());
        assert(compressed_solver.rectangles.empty());

        const auto [value, x, y] =
            solver.calc_max_lexicographically_minimum_point();
        const auto [area_value, area] = solver.calc_max_area<long long>();
        assert(value == 0);
        assert(x == 0);
        assert(y == 0);
        assert(area_value == 0);
        assert(area == 0);
    }
}
} // namespace

int main() {
    self_test();
    return 0;
}
Back to top page