LG6109 [Ynoi2009] rprmq1

发布于 2022-03-13  2.93k 次阅读


题目链接

Statement

有一个 $n \times n$ 的矩阵 $a$,初始全是 $0$,有 $m$ 次修改操作和 $q$ 次查询操作,先进行所有修改操作,然后进行所有查询操作。

一次修改操作会给出 $l_1,~l_2,~r_1,~r_2,~x$,代表把所有满足 $l_1 \le i \le r_1$ 且 $l_2 \le j \le r_2$ 的 $a_{i,j}$ 元素加上一个值 $x$。

一次查询操作会给出 $l_1,~l_2,~r_1,~r_2$,代表查询所有满足 $l_1 \le i \le r_1$ 且 $l_2 \le j \le r_2$ 的 $a_{i,j}$ 元素的最大值。

$1 \le n,~m \le 5 \times 10^4,~1 \le q \le 5 \times 10^5$

Solution

由于询问数量远高于修改数量,考虑询问复杂度较低的算法。

考虑在 $X$ 轴上分治,对于分治区间 $[l,~r]$ 我们求出所有 $l_1 \le mid = \lfloor \frac {l + r} 2 \rfloor \le r_1$ 的询问的答案。可以从 $mid$ 开始向左做一次扫描线,维护区间加减的同时维护区间历史最大值即可得到 $\max_{l_1 \le i \le mid,~l_2 \le j \le r_2} a_{i,~j}$ 的值。同理再从 $mid$ 开始向右做一次扫描线,将 $[l_1,~mid]$ 与 $[mid,~r_1]$ 的答案取较大值即可得到原询问的答案。

为保持分治结构每层增减修改操作的数量保持在 $O(n)$ 级别,考虑按该分治结构建立线段树,对于每个修改操作,按线段树插入的方法插入到线段树中。插入操作最终到达的 $\log n$ 个被 $[l_1,~r_1]$ 包含的结点采用类似标记永久化的方法,线段树分治进入该结点子树时加入该修改操作贡献,离开该结点子树时减去该修改操作贡献。对于线段树上其他 $O(\log n)$ 个存在一部分被修改操作包含的结点,在其内分治执行扫描线过程时最多会进行 $2$ 次区间修改操作,因此全局花在维护修改操作贡献上的时间复杂度为 $O(m \log^2 n)$。

总时间复杂度 $O(m \log^2 n + q \log n)$。

Code

View on GitHub

/**
 * @file 6109.cpp
 * @author Macesuted (i@macesuted.moe)
 * @date 2022-03-11
 *
 * @copyright Copyright (c) 2022
 *
 */

#include <bits/stdc++.h>
using namespace std;

namespace IO {
const int SIZE = 1 << 20;
char Ibuf[SIZE], *Il = Ibuf, *Ir = Ibuf, Obuf[SIZE], *Ol = Obuf, *Or = Ol + SIZE - 1, stack[32];
char isspace(char c) { return c == ' ' || c == 't' || c == 'n' || c == 'v' || c == 'f' || c == 'r'; }
void fill(void) { return Ir = (Il = Ibuf) + fread(Ibuf, 1, SIZE, stdin), void(); }
void flush(void) { return fwrite(Obuf, 1, Ol - Obuf, stdout), Ol = Obuf, void(); }
char buftop(void) { return Ir == Il ? fill(), *Il : *Il; }
char getch(void) { return Il == Ir ? fill(), Il == Ir ? EOF : *Il++ : *Il++; }
void putch(char x) { return *Ol++ = x, Ol == Or ? flush() : void(); }
template <typename T>
T read(void) {
    T x = 0, f = +1;
    char c = getch();
    while (c < '0' || c > '9') c == '-' ? void(f = -f) : void(), c = getch();
    while ('0' <= c && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48), c = getch();
    return x * f;
}
template <typename T>
void write(T x) {
    if (!x) putch('0');
    if (x < 0) putch('-'), x = -x;
    int top = 0;
    while (x) stack[top++] = (x % 10) ^ 48, x /= 10;
    while (top) putch(stack[--top]);
    return;
}
string getstr(const string& suf = "") {
    string s = suf;
    while (isspace(buftop())) getch();
    while (Il != Ir) {
        char* p = Il;
        while (Il < Ir && !isspace(*Il) && *Il != EOF) Il++;
        s.append(p, Il);
        if (Il < Ir) break;
        fill();
    }
    return s;
}
void putstr(string str, int begin = 0, int end = -1) {
    if (end == -1) end = str.size();
    for (int i = begin; i < end; i++) putch(str[i]);
    return;
}
struct Flusher_ {
    ~Flusher_() { flush(); }
} io_flusher_;
}  // namespace IO
using IO::getch;
using IO::getstr;
using IO::putch;
using IO::putstr;
using IO::read;
using IO::write;

bool mem1;

#define maxn 50005
#define maxq 500005

typedef pair<int, int> pii;
typedef tuple<int, int, int> tiii;
typedef tuple<int, int, int, int, int> tiiiii;

vector<tiii> rec[2][maxn];
long long ans[maxq];

class SegmentTree1 {
   private:
    class SegmentTree {
       private:
        struct Node {
            long long maxVal, maxVal_, lazy, lazy_;
            bool clearHist;
        };

        Node tree[maxn << 4];
        int n;

        void clearHist(int p) {
            if (tree[p].lazy_ || tree[p].lazy)
                calcLazy(p << 1, tree[p].lazy_, tree[p].lazy), calcLazy(p << 1 | 1, tree[p].lazy_, tree[p].lazy);
            return tree[p].maxVal_ = tree[p].maxVal, tree[p].lazy_ = tree[p].lazy = 0, tree[p].clearHist = true, void();
        }
        void calcLazy(int p, long long lazy_, long long lazy) {
            return tree[p].maxVal_ = max(tree[p].maxVal_, tree[p].maxVal + lazy_), tree[p].maxVal += lazy,
                   tree[p].lazy_ = max(tree[p].lazy_, tree[p].lazy + lazy_), tree[p].lazy += lazy, void();
        }
        void pushDown(int p) {
            if (tree[p].clearHist) clearHist(p << 1), clearHist(p << 1 | 1), tree[p].clearHist = false;
            if (tree[p].lazy_ || tree[p].lazy)
                calcLazy(p << 1, tree[p].lazy_, tree[p].lazy), calcLazy(p << 1 | 1, tree[p].lazy_, tree[p].lazy),
                    tree[p].lazy_ = tree[p].lazy = 0;
            return;
        }
        void pushUp(int p) {
            return tree[p].maxVal = max(tree[p << 1].maxVal, tree[p << 1 | 1].maxVal),
                   tree[p].maxVal_ = max(tree[p << 1].maxVal_, tree[p << 1 | 1].maxVal_), void();
        }
        void add(int p, int l, int r, int ql, int qr, long long v) {
            if (ql <= l && r <= qr) return calcLazy(p, max(0LL, v), v);
            pushDown(p);
            int mid = (l + r) >> 1;
            if (ql <= mid) add(p << 1, l, mid, ql, qr, v);
            if (qr > mid) add(p << 1 | 1, mid + 1, r, ql, qr, v);
            return pushUp(p);
        }
        void clearHist(int p, int l, int r, int ql, int qr) {
            if (ql <= l && r <= qr) return clearHist(p);
            pushDown(p);
            int mid = (l + r) >> 1;
            if (ql <= mid) clearHist(p << 1, l, mid, ql, qr);
            if (qr > mid) clearHist(p << 1 | 1, mid + 1, r, ql, qr);
            return pushUp(p);
        }

        long long getHistMaxVal(int p, int l, int r, int ql, int qr) {
            if (ql <= l && r <= qr) return tree[p].maxVal_;
            pushDown(p);
            int mid = (l + r) >> 1;
            if (qr <= mid) return getHistMaxVal(p << 1, l, mid, ql, qr);
            if (ql > mid) return getHistMaxVal(p << 1 | 1, mid + 1, r, ql, qr);
            return max(getHistMaxVal(p << 1, l, mid, ql, qr), getHistMaxVal(p << 1 | 1, mid + 1, r, ql, qr));
        }

       public:
        void resize(int _n) { return n = _n, void(); }
        void add(int l, int r, long long v) { return add(1, 1, n, l, r, v); }
        void clearHist(int l, int r) { return clearHist(1, 1, n, l, r); }
        long long getHistMaxVal(int l, int r) { return getHistMaxVal(1, 1, n, l, r); }
    } ST;

    struct Node {
        vector<tiii> whole, init;
        vector<tiiiii> query;
    };

    Node tree[maxn << 2];
    int n;

    void insRec(int p, int l, int r, int ql, int qr, tiii v) {
        if (ql <= l && r <= qr) return tree[p].whole.push_back(v);
        int mid = (l + r) >> 1;
        if (ql <= mid && mid <= qr) tree[p].init.push_back(v);
        if (ql <= mid) insRec(p << 1, l, mid, ql, qr, v);
        if (qr > mid) insRec(p << 1 | 1, mid + 1, r, ql, qr, v);
        return;
    }
    void insQues(int p, int l, int r, tiiiii ques) {
        int mid = (l + r) >> 1;
        if ((l <= get<0>(ques) && get<0>(ques) <= mid && mid < get<1>(ques) && get<1>(ques) <= r) || l == r)
            return tree[p].query.push_back(ques);
        return get<1>(ques) <= mid ? insQues(p << 1, l, mid, ques) : insQues(p << 1 | 1, mid + 1, r, ques);
    }
    void solve(int p, int l, int r) {
        if (l == r) {
            for (auto i : tree[p].whole) ST.add(get<0>(i), get<1>(i), get<2>(i));
            ST.clearHist(1, n);
            for (auto i : tree[p].query) ans[get<4>(i)] = max(ans[get<4>(i)], ST.getHistMaxVal(get<2>(i), get<3>(i)));
            for (auto i : tree[p].whole) ST.add(get<0>(i), get<1>(i), -get<2>(i));
            return;
        }
        for (auto i : tree[p].whole) ST.add(get<0>(i), get<1>(i), get<2>(i));
        int mid = (l + r) >> 1;
        for (auto i : tree[p].init) ST.add(get<0>(i), get<1>(i), get<2>(i));
        static vector<tiii> cache;
        cache.clear(), ST.clearHist(1, n);
        sort(tree[p].query.begin(), tree[p].query.end(), [](tiiiii a, tiiiii b) { return get<0>(a) > get<0>(b); });
        auto j = tree[p].query.begin();
        while (j != tree[p].query.end() && get<0>(*j) == mid)
            ans[get<4>(*j)] = max(ans[get<4>(*j)], ST.getHistMaxVal(get<2>(*j), get<3>(*j))), j++;
        for (int i = mid - 1; i >= l; i--) {
            for (auto j : rec[0][i + 1])
                ST.add(get<0>(j), get<1>(j), -get<2>(j)), cache.emplace_back(get<0>(j), get<1>(j), -get<2>(j));
            for (auto j : rec[1][i]) ST.add(get<0>(j), get<1>(j), get<2>(j)), cache.push_back(j);
            while (j != tree[p].query.end() && get<0>(*j) == i)
                ans[get<4>(*j)] = max(ans[get<4>(*j)], ST.getHistMaxVal(get<2>(*j), get<3>(*j))), j++;
        }
        for (auto i = cache.rbegin(); i != cache.rend(); i++) ST.add(get<0>(*i), get<1>(*i), -get<2>(*i));
        cache.clear(), ST.clearHist(1, n);
        sort(tree[p].query.begin(), tree[p].query.end(), [](tiiiii a, tiiiii b) { return get<1>(a) < get<1>(b); });
        j = tree[p].query.begin();
        while (j != tree[p].query.end() && get<1>(*j) == mid)
            ans[get<4>(*j)] = max(ans[get<4>(*j)], ST.getHistMaxVal(get<2>(*j), get<3>(*j))), j++;
        for (int i = mid + 1; i <= r; i++) {
            for (auto j : rec[1][i - 1])
                ST.add(get<0>(j), get<1>(j), -get<2>(j)), cache.emplace_back(get<0>(j), get<1>(j), -get<2>(j));
            for (auto j : rec[0][i]) ST.add(get<0>(j), get<1>(j), get<2>(j)), cache.push_back(j);
            while (j != tree[p].query.end() && get<1>(*j) == i)
                ans[get<4>(*j)] = max(ans[get<4>(*j)], ST.getHistMaxVal(get<2>(*j), get<3>(*j))), j++;
        }
        for (auto i = cache.rbegin(); i != cache.rend(); i++) ST.add(get<0>(*i), get<1>(*i), -get<2>(*i));
        for (auto i : tree[p].init) ST.add(get<0>(i), get<1>(i), -get<2>(i));
        solve(p << 1, l, mid), solve(p << 1 | 1, mid + 1, r);
        for (auto i : tree[p].whole) ST.add(get<0>(i), get<1>(i), -get<2>(i));
        return;
    }

   public:
    void resize(int _n) { return n = _n, void(); }
    void insRec(int l, int r, tiii v) { return insRec(1, 1, n, l, r, v); }
    void insQues(tiiiii ques) { return insQues(1, 1, n, ques); }
    void solve(void) { return ST.resize(n), solve(1, 1, n); }
} ST;

void solve(void) {
    ios::sync_with_stdio(false);
    int n = read<int>(), m = read<int>(), q = read<int>();
    ST.resize(n);
    for (int i = 1; i <= m; i++) {
        int l1 = read<int>(), r1 = read<int>(), l2 = read<int>(), r2 = read<int>(), x = read<int>();
        rec[0][l1].emplace_back(r1, r2, x), rec[1][l2].emplace_back(r1, r2, x), ST.insRec(l1, l2, {r1, r2, x});
    }
    for (int i = 1; i <= q; i++) {
        int l1 = read<int>(), r1 = read<int>(), l2 = read<int>(), r2 = read<int>();
        ST.insQues({l1, l2, r1, r2, i});
    }
    ST.solve();
    for (int i = 1; i <= q; i++) write(ans[i]), putch('n');
    return;
}

bool mem2;

int main() {
#ifdef MACESUTED
    cerr << "Memory Cost: " << abs(&mem1 - &mem2) / 1024. / 1024. << "MB" << endl;
#endif

    int _ = 1;
    while (_--) solve();

#ifdef MACESUTED
    cerr << "Time Cost: " << clock() * 1000. / CLOCKS_PER_SEC << "MS" << endl;
#endif
    return 0;
}

我缓慢吐出一串啊吧啊吧并不再想说话