Submission #1177331

#TimeUsernameProblemLanguageResultExecution timeMemory
1177331JohnTitorTwo Antennas (JOI19_antennas)C++20
100 / 100
392 ms27728 KiB
#include <bits/stdc++.h>
using namespace std;

// offline processing
// - process query by increasing r
// - when we add a new element r, we update ans[l], which is the best result starting from l for each l
// -
// - consider the elements r can look at
//
// we want a segment tree that will handle the following:
// - hold 2 arrays a[i] and c[i]
// - query the max of a[i] - c[i]
// - update a range a[i] = max(a[i], x)
// - if we start with a = 0 normally then we can get the answer from max - min everytime we update x

constexpr int INF = 1e9 + 1;
int n;
constexpr int MAX_N = 2e5;

// answer for the positions that's already done
int ft[MAX_N + 1];
void update_done(int u, int result) {
    for (; u > 0; u -= u & -u) ft[u] = max(ft[u], result);
}

int get_done(int u) {
    int res = -INF;
    for (; u <= n; u += u & -u) res = max(res, ft[u]);
    return res;
}

class segment_tree_t {
public:
    class node_t {
    public:
        int max_a;
        int max_minus;
        int min_c;
    } st[MAX_N * 4];

    void build(int i = 1, int l = 1, int r = n) {
        auto &&[max_a, max_minus, min_c] = st[i];

        max_a = -INF;
        max_minus = -INF;
        min_c = INF;
        if (l != r) {
            int m = (l + r) / 2;
            build(i * 2, l, m);
            build(i * 2 + 1, m + 1, r);
        }
    }

    void recalculate(int i, int l, int r) {
        auto &&[max_a, max_minus, min_c] = st[i];
        if (l != r) {
            auto &&left = st[i * 2];
            auto &&right = st[i * 2 + 1];
            min_c = min(left.min_c, right.min_c);
            max_minus = max(left.max_minus, right.max_minus);
        } else {
            max_minus = -INF;
        }
        max_minus = max(max_minus, max_a - min_c);
    }

    void down(int i) {
        auto &&[max_a, max_minus, min_c] = st[i];
        auto &&left = st[i * 2];
        auto &&right = st[i * 2 + 1];
        left.max_a = max(left.max_a, max_a);
        left.max_minus = max(left.max_minus, max_a - left.min_c);
        right.max_a = max(right.max_a, max_a);
        right.max_minus = max(right.max_minus, max_a - right.min_c);
        max_a = -INF;
    }

    void update_c(int i, int l, int r, int u, int c) {
        if (l == r) {
            auto &&[max_a, max_minus, min_c] = st[i];
            assert(l == u);
            min_c = c;
            max_a = -INF;
        } else {
            down(i);
            int m = (l + r) / 2;
            if (m >= u) update_c(i * 2, l, m, u, c);
            else update_c(i * 2 + 1, m + 1, r, u, c);
        }
        recalculate(i, l, r);
    }

    void update_a(int i, int l, int r, int u, int v, int a) {
        if (l > v || r < u) return;
        if (u <= l && v >= r) {
            auto &&[max_a, max_minus, min_c] = st[i];
            max_a = max(max_a, a);
            recalculate(i, l, r);
        } else {
            down(i);
            int m = (l + r) / 2;
            update_a(i * 2, l, m, u, v, a);
            update_a(i * 2 + 1, m + 1, r, u, v, a);
            recalculate(i, l, r);
        }
    }

    int get_max_minus(int i, int l, int r, int u, int v) {
        if (l > v || r < u) return -INF;
        auto &&[max_a, max_minus, min_c] = st[i];
        if (u <= l && v >= r) return max_minus;
        down(i);
        int m = (l + r) / 2;
        return max(get_max_minus(i * 2, l, m, u, v), get_max_minus(i * 2 + 1, m + 1, r, u, v));
    }
} tree;

int q;
int h[MAX_N + 1];
int a[MAX_N + 1];
int b[MAX_N + 1];
int ans[MAX_N + 1];

vector<int> turn_on[MAX_N + 1];
vector<int> turn_off[MAX_N + 1];

vector<tuple<int, int, int>> queries;
void solve() {
    tree.build();
    int i = 1;
    fill(ft + 1, ft + n + 1, -INF);
    for (auto &&[r, l, id] : queries) {
        while (i <= r) {
            for (auto &&j : turn_on[i]) tree.update_c(1, 1, n, j, h[j]);
            for (auto &&j : turn_off[i]) {
                // this element is done, it can't be used to update the answer anymore, but the existing answer can still be
                // used
                update_done(j, tree.get_max_minus(1, 1, n, j, j));
                tree.update_c(1, 1, n, j, INF);
            }
            int l_i = i - b[i];
            int r_i = i - a[i];
            l_i = max(l_i, 1);
            if (l_i <= r_i) tree.update_a(1, 1, n, l_i, r_i, h[i]);
            i++;
        }
        // to answer a query, we use the existing answer and the done answer
        ans[id] = max(ans[id], get_done(l));
        ans[id] = max(ans[id], tree.get_max_minus(1, 1, n, l, r));
    }
}

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    cin >> n;
    assert(n <= 2e5);
    for (int i = 1; i <= n; i++) cin >> h[i] >> a[i] >> b[i];
    for (int i = 1; i <= n; i++) {
        int l = i + a[i];
        int r = i + b[i];
        if (l <= n) turn_on[l].push_back(i);
        if (r + 1 <= n) turn_off[r + 1].push_back(i);
    }
    cin >> q;
    assert(q <= 2e5);
    for (int i = 1; i <= q; i++) {
        static int l, r;
        cin >> l >> r;
        queries.emplace_back(r, l, i);
        assert(l <= r);
    }
    sort(queries.begin(), queries.end());
    fill(ans + 1, ans + q + 1, -1);
    solve();
    for (int i = 1; i <= n; i++) h[i] = -h[i];
    solve();
    for (int i = 1; i <= q; i++) cout << ans[i] << '\n';
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...