线段树分治 笔记

本质上,这只是一种结合了线段树和 CDQ 分治的思想的东西,和线段树并没有太大的关联。

P4585 [FJOI2015] 火星商店问题

首先,如果不考虑时间的问题,也就是说加入了所有商品后,再进行询问。那么按商店从小到大加到对应的 01 Trie 里,可持久化即可解决问题。

接下来考虑时间问题,先把每个询问的时间区间都打到一个时间轴上,将这个时间轴同时看作线段树维护的区间,处理一个询问,就按照线段树的方式把它分割成若干个询问。

如时间轴总长度为 \(5\),询问 \([2,5]\to\) 询问 \([2,2],[3,3],[4,5]\)

把询问挂到线段树上后,考虑每个线段树区间包含哪些修改。

某个时间区间包含的修改一定是影响这个区间里所有的询问的,因为修改是区间内一点,而询问覆盖了整个区间。

将这个时间区间的修改打到可持久化 Trie 上,询问即可。

然后按照线段树的定义,把修改拆成左右两半,继续递归。

特殊商品的加入时间可以看作 \(0\),但并不用把特殊商品放到线段树上,只需要在最初还没把询问挂到线段树的时候,单独处理每个询问对于特殊商品的答案即可。

struct Q0 {
    int s, v, t;
    bool operator<(const Q0 &x) const {
        return s < x.s;
    }
} q0[N], q0l[N], q0r[N];
struct Q1 {
    int l, r, tl, tr, x;
} q1[N];

namespace TRIE { ... } // 可持久化 Trie
using namespace TRIE;

namespace Segtree {
#define ls rt << 1
#define rs rt << 1 | 1
    vector<int> tr[N];
    int st[N], top;
    void update(int rt, int l, int r, int x, int y, int v) {
        if (l > r or l > y or r < x)
            return;
        if (x <= l and r <= y) {
            tr[rt].push_back(v); // 把询问按线段树的区间拆分
            return;
        }
        int mid = l + r >> 1;
        update(ls, l, mid, x, y, v);
        update(rs, mid + 1, r, x, y, v);
    }
    void calc(int rt, int l0, int r0) {
        idx = top = 0;
        for (int i = l0; i <= r0; i++) {
            st[++top] = q0[i].s;
            root[top] = ++idx;
            insert(root[top], root[top - 1], q0[i].v);
        }
        for (int i : tr[rt]) {
            // 下面这两个,是先 upper_bound,再减去 1,等价于找第一个小于等于 x 的数,这样子才能真正包含询问的商店区间
            int l = upper_bound(st + 1, st + top + 1, q1[i].l - 1) - st - 1;
            int r = upper_bound(st + 1, st + top + 1, q1[i].r) - st - 1;
            ans[i] = max(ans[i], query(root[l], root[r], q1[i].x));
        }
    }
    void solve(int rt, int tl, int tr, int l0, int r0) {
        if (l0 > r0)
            return;
        calc(rt, l0, r0);
        if (tl == tr)
            return;
        int cl = 0, cr = 0;
        int tmid = tl + tr >> 1;
        for (int i = l0; i <= r0; i++) // 按照时间把修改分成左右两半
            if (q0[i].t <= tmid)
                q0l[++cl] = q0[i];
            else
                q0r[++cr] = q0[i];
        for (int i = l0, j = 1; j <= cl; i++, j++)
            q0[i] = q0l[j];
        for (int i = l0 + cl, j = 1; j <= cr; i++, j++)
            q0[i] = q0r[j];
        solve(ls, tl, tmid, l0, l0 + cl - 1);
        solve(rs, tmid + 1, tr, l0 + cl, r0);
    }
}
using namespace Segtree;

signed main() {
    IOS;
    cin >> n >> m;
    for (int i = 1, x; i <= n; i++) {
        cin >> x;
        root[i] = ++idx;
        insert(root[i], root[i - 1], x);
    }
    for (int i = 1, opt, l, r, x, d; i <= m; i++) {
        cin >> opt;
        if (!opt) {
            cin >> x >> d;
            q0[++n0] = {x, d, n0};
        } else {
            cin >> l >> r >> x >> d;
            q1[++n1] = {l, r, max(1ll, n0 - d + 1), n0, x};
            ans[n1] = query(root[l - 1], root[r], x); // 提前处理特殊商品
        }
    }
    for (int i = 1; i <= n1; i++)
        update(1, 1, n0, q1[i].tl, q1[i].tr, i);
    sort(q0 + 1, q0 + n0 + 1);
    solve(1, 1, n0, 1, n0);
    for (int i = 1; i <= n1; i++)
        cout << ans[i] << "\n";
    return 0;
}
posted @ 2025-02-22 08:27  Garbage_fish  阅读(19)  评论(0)    收藏  举报