QOJ971 Binary Search Tree

QOJ971 Binary Search Tree

维护 \(n\) 棵二叉搜索树 \(T_1 \sim T_n\),支持 \(q\) 次操作:

  • 给定 \(l,r,x\),对 \(i \in [l,r]\) 向 \(T_i\) 中插入 \(x\)。保证 \(x\) 两两不同。
  • 给定 \(i,x\),在 \(T_i\) 上递归寻找 \(x\),查询访问的所有节点的权值和。

\(n,q \le 2\times 10^5\),2 秒,256 MB。

换维扫描线,维护时间轴。固定 \(i\),设 \(a_t=x\) 表示 \(t\) 时刻插入 \(T_i\) 的值,扫到 \(l\) 和 \(r+1\) 时进行切换。

查询给出时间前缀 \(1 \sim r\),依次将 \(a_1 \sim a_r\) 插入 \(T_i\)。

image.png

维护 \(b_x=t\) 表示 \(x\) 的插入时刻,若 \(x\) 始终未插入视作 \(+\infty\)。查询 \(1 \sim r\) 时 \(T_i=\{x \mid b_x\le r\}\)。

建立 \(b_x\) 的小根笛卡尔树。注意到 BST 本质是插入时间为优先级的 Treap,时间序列换维后,笛卡尔树和 \(T_i\) 等价。

查询 \(x\) 时访问的节点是 \(x \leadsto \text{root}\) 根链,取所有 \(b_x \le r\) 的节点权值和。笛卡尔树上 \(b\) 值递增,找到最深的 \(b_x \le r\) 的祖先 \(y\),取根链和即为答案。

将问题拍到 \(b\) 序列上,只要找出 \(y\le x\land b_y \le r\) 的最大的 \(y\),查询 \([1,y]\) 中后缀 \(\min\) 的带权和即可。对右侧同理求解。

单侧递归线段树维护区间 \(\min\),支持前后缀 \(\min\) 带权和即可。复杂度 \(O(q \log^2 n)\)。

const int N = 2e5 + 5, inf = 1e9;
int n, q, m, b[N]; i64 ans[N];

struct discrete {
    vector<int> d;
    void add(int x) {d.pb(x);}
    void init() {
        sort(all(d)), d.erase(unique(all(d)), d.end());
        m = d.size();
    }
    int operator[] (int x) const {
        return lower_bound(all(d), x) - d.begin() + 1;
    }
    int val(int x) {return d[x-1];}
} D;
vector<tuple<int, int, int>> P[N];  // (t, x, op)
vector<pair<int, int>> Q[N];        // (t, x)

struct segtree {
    #define ls lc[rt]
    #define rs rc[rt]
    int tot = 1, val[4*N], lc[4*N], rc[4*N]; 
    i64 pre[4*N], suf[4*N];
    
    i64 calcl(int x, int rt) {
        if (x <= val[rt]) return 0;
        if (!ls) return suf[rt];
        if (x <= val[rs]) return calcl(x, ls);
        return calcl(x, rs) + suf[rt] - suf[rs];
    }
    i64 calcr(int x, int rt) {
        if (x <= val[rt]) return 0;
        if (!ls) return pre[rt];
        if (x <= val[ls]) return calcr(x, rs);
        return pre[rt] - pre[ls] + calcr(x, ls);
    }
    void pushup(int rt) {
        val[rt] = min(val[ls], val[rs]);
        pre[rt] = pre[ls] + calcr(val[ls], rs);
        suf[rt] = calcl(val[rs], ls) + suf[rs];
    }
    void set(int rt, int c, int x) {
        val[rt] = c, ls = rs = 0;
        pre[rt] = suf[rt] = (x < 0 || c == inf) ? 0 : D.val(x);
    }
    void build(int l = 1, int r = m, int rt = 1) {
        if (l == r) return set(rt, inf, l);
        int mid = (l + r) >> 1; ls = ++tot, rs = ++tot;
        build(l, mid, ls), build(mid+1, r, rs), pushup(rt);
    }
    void update(int x, int c, int l = 1, int r = m, int rt = 1) {
        if (l == r) return set(rt, c, l);
        int mid = (l + r) >> 1;
        if (x <= mid) update(x, c, l, mid, ls);
        else update(x, c, mid+1, r, rs);
        pushup(rt);
    }
    int qmin(int tl, int tr, int l = 1, int r = m, int rt = 1) {
        if (tl <= l && r <= tr) return val[rt];
        int mid = (l + r) >> 1;
        if (tl <= mid && mid < tr) return min(qmin(tl, tr, l, mid, ls), qmin(tl, tr, mid+1, r, rs));
        return tl <= mid ? qmin(tl, tr, l, mid, ls) : qmin(tl, tr, mid+1, r, rs);
    }
    i64 qpre(int tl, int tr, int x) {
        if (tl > tr) return 0;
        vector<int> vec;
        auto qry = [&](auto&& qry, int l, int r, int rt) -> void {
            if (tl <= l && r <= tr) return vec.pb(rt), void();
            int mid = (l + r) >> 1;
            if (tl <= mid) qry(qry, l, mid, ls);
            if (tr > mid) qry(qry, mid+1, r, rs);
        };
        qry(qry, 1, m, 1);
        i64 res = 0;
        for (int rt : vec) {
            if (x <= val[rt]) continue;
            res += calcr(x, rt), x = val[rt];
        }
        return res;
    }
    i64 qsuf(int tl, int tr, int x) {
        if (tl > tr) return 0;
        vector<int> vec;
        auto qry = [&](auto&& qry, int l, int r, int rt) -> void {
            if (tl <= l && r <= tr) return vec.pb(rt), void();
            int mid = (l + r) >> 1;
            if (tl <= mid) qry(qry, l, mid, ls);
            if (tr > mid) qry(qry, mid+1, r, rs);
        };
        qry(qry, 1, m, 1); reverse(all(vec));
        i64 res = 0;
        for (int rt : vec) {
            if (x <= val[rt]) continue;
            res += calcl(x, rt), x = val[rt];
        }
        return res;
    }
} T;

void _main() {
    cin >> n >> q;
    vector<int> vec;
    for (int i = 1, op, l, r, x; i <= q; i++) {
        cin >> op;
        if (op == 1) {
            cin >> l >> r >> x, D.add(x);
            P[l].pb(i, x, +1), P[r+1].pb(i, x, -1);
        } else {
            cin >> r >> x, D.add(x);
            Q[r].pb(i, x), vec.pb(i);
        }
    }
    D.init(), T.build(), fill(b + 1, b + m + 1, inf);
    for (int i = 1; i <= n; i++) {
        auto ask = [](int x, int r) -> i64 {
            i64 res = 0;
            int lo = 1, hi = x, y = -1;
            while (lo <= hi) {
                int mid = (lo + hi) >> 1;
                if (T.qmin(mid, x) <= r) y = mid, lo = mid+1;
                else hi = mid-1;
            }
            res += T.qsuf(1, y, inf);
            lo = x, hi = m, y = m+1;
            while (lo <= hi) {
                int mid = (lo + hi) >> 1;
                if (T.qmin(x, mid) <= r) y = mid, hi = mid-1;
                else lo = mid+1;
            }
            res += T.qpre(y, m, b[x] <= r ? b[x] : inf);
            return res;
        };
        for (auto [t, x, op] : P[i]) {
            b[D[x]] = (op == 1 ? t : inf);
            T.update(D[x], b[D[x]]);
        }
        for (auto [t, x] : Q[i]) 
            ans[t] = ask(D[x], t); 
    }
    for (int i : vec) cout << ans[i] << '\n'; 
} 
posted @ 2026-11-03 21:01  stripe_python  阅读(12)  评论(0)    收藏  举报