久未放晴的天空,|

TulipeNoire

园龄:1年10个月粉丝:18关注:17

「PR #15」二叉搜索树

考虑询问算的是个什么东西:假设我们在一个点处按时间顺序插入的数的序列为 a,查询的值是 x,那么本质上来说,是要找 a 序列比 x 大的部分的前缀最大值之和,以及比 x 小的部分的前缀最小值之和(还要特判 x)。以值域为下标维护这个数的插入时间。那么我们要算的是 [x,+) 区间的前缀最小值的下标之和,以及 (,x] 的后缀最小值的下标之和。对每个节点开棵兔队线段树即可过掉菊花(修改数很少)的部分分。

考虑怎么推广到链加。考虑把操作离线下来,利用树上差分的思想,用线段树合并维护出每个节点的线段树长什么样(在端点插入,在 LCA 处删)。但是这样在查的时候要限制产生贡献的值必须在这个查询操作之前出现。仔细想一下,其实这个限制也可以直接对查询加深限制。具体地,我们令当前询问在时间轴上的位置是 t,那么直接在查询之前让“之前的区间最值”为 t 就可以了(一般来讲是极大/小值)。

分析合并兔队线段树的复杂度。每次 pushup 时必定合并掉了一个节点,所以复杂度就是 O(nlogn+qlog2n),空间复杂度 O(n+qlogn)

然后就完了,感觉很简单,但是场上竟然没想到最后一步。并且这个题的通过情况也不是很好,挺抽象的。是不是大家都没想到兔队线段树可以合并?

#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
using LL = long long;
using pii = pair<int, int>;
inline int read() {
    int f = 0;
    char c = getchar();
    while (!isdigit(c)) c = getchar();
    while (isdigit(c)) f = f * 10 + (c - '0'), c = getchar();
    return f;
}
const int N = 200005, lim = 200000, inf = 1e9;
int n, m;
vector<int> G[N];
int fa[N][21], dep[N], op[N], w[N];
void dfs1(int p, int lst) {
    fa[p][0] = lst, dep[p] = dep[lst] + 1;
    for (int i = 1; i <= 20; i++) fa[p][i] = fa[fa[p][i - 1]][i - 1];
    for (auto x : G[p]) {
        if (x != lst) dfs1(x, p);
    }
}
inline int lca(int x, int y) {
    if (dep[x] < dep[y]) swap(x, y);
    for (int i = 20; i >= 0; i--) if (dep[fa[x][i]] >= dep[y]) x = fa[x][i];
    if (x == y) return x;
    for (int i = 20; i >= 0; i--) if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
    return fa[x][0];
}
inline int jump(int x, int k) {
    for (int i = 0; i <= 20; i++) if ((k >> i) & 1) x = fa[x][i];
    return x;
}
vector<int> add[N], del[N];
vector<pii> qry[N];
LL ans[N];
struct Node {
    int lc, rc, m;
    LL f;
};
int rt[2][N], cnt;
Node tr[N * 80];
LL query(int p, int l, int r, int lm) {
    if (l == r) return tr[p].m < lm ? tr[p].f : 0;
    int mid = (l + r) >> 1;
    if (tr[tr[p].lc].m >= lm) return query(tr[p].rc, mid + 1, r, lm);
    return query(tr[p].lc, l, mid, lm) + tr[p].f - tr[tr[p].lc].f;
}
inline void pushup(int p, int l, int r) {
    int mid = (l + r) >> 1;
    tr[p].m = min(tr[tr[p].lc].m, tr[tr[p].rc].m);
    tr[p].f = tr[tr[p].lc].f + query(tr[p].rc, mid + 1, r, tr[tr[p].lc].m);
}
void update(int &p, int l, int r, int x, int f, int w) {
    if (!p) p = ++cnt;
    if (l == r) {
        tr[p].f = f, tr[p].m = w;
        return;
    }
    int mid = (l + r) >> 1;
    if (x <= mid) update(tr[p].lc, l, mid, x, f, w);
    else update(tr[p].rc, mid + 1, r, x, f, w);
    pushup(p, l, r);
}
int Merge(int p, int q, int l, int r) {
    if (!p || !q) return p + q;
    if (l == r) {
        tr[p].f += tr[q].f;
        tr[p].m = min(tr[p].m, tr[q].m);
        return p;
    }
    int mid = (l + r) >> 1;
    tr[p].lc = Merge(tr[p].lc, tr[q].lc, l, mid);
    tr[p].rc = Merge(tr[p].rc, tr[q].rc, mid + 1, r);
    pushup(p, l, r);
    return p;
}
int lm_;
LL qans(int p, int l, int r, int L, int R) {
    if (L <= l && r <= R) {
        LL ans = query(p, l, r, lm_);
        lm_ = min(lm_, tr[p].m);
        return ans;
    }
    int mid = (l + r) >> 1;
    LL res = 0;
    if (L <= mid) res += qans(tr[p].lc, l, mid, L, R);
    if (R > mid) res += qans(tr[p].rc, mid + 1, r, L, R);
    return res;
}
void dfs2(int p, int lst) {
    for (auto x : add[p]) {
        update(rt[0][p], 1, lim, w[x], w[x], x);
        update(rt[1][p], 1, lim, lim - w[x] + 1, w[x], x);
    }
    for (auto x : G[p]) {
        if (x != lst) {
            dfs2(x, p);
            rt[0][p] = Merge(rt[0][p], rt[0][x], 1, lim);
            rt[1][p] = Merge(rt[1][p], rt[1][x], 1, lim);
        }
    }
    for (auto x : qry[p]) {
        lm_ = x.se;
        ans[x.se] += qans(rt[0][p], 1, lim, x.fi, lim);
        lm_ = x.se;
        ans[x.se] += qans(rt[1][p], 1, lim, lim - x.fi + 1, lim);
        lm_ = x.se;
        ans[x.se] -= qans(rt[0][p], 1, lim, x.fi, x.fi);
    }
    for (auto x : del[p]) {
        update(rt[0][p], 1, lim, w[x], 0, inf);
        update(rt[1][p], 1, lim, lim - w[x] + 1, 0, inf);
    }
}
int main() {
    n = read(), m = read(), tr[0].m = inf;
    for (int i = 1; i < n; i++) {
        int x = read(), y = read();
        G[x].push_back(y);
        G[y].push_back(x);
    }
    dfs1(1, 0);
    for (int i = 1; i <= m; i++) {
        op[i] = read();
        if (op[i]) {
            int u = read(), v = read();
            w[i] = read();
            int t = lca(u, v);
            add[u].push_back(i);
            del[t].push_back(i);
            if (v != t) {
                add[v].push_back(i);
                del[jump(v, dep[v] - dep[t] - 1)].push_back(i);
            }
        }
        else {
            int x = read(), y = read();
            qry[x].push_back({y, i});
        }
    }
    dfs2(1, 0);
    for (int i = 1; i <= m; i++) if (!op[i]) printf("%lld\n", ans[i]);
    return 0;
}

本文作者:TulipeNoire

本文链接:https://www.cnblogs.com/TulipeNoire/p/18717065/ZZZ

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   TulipeNoire  阅读(14)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起