CC COT5 [线段树][Treap]

本文介绍了一种高级数据结构——Treap,支持插入、删除及查询两点间距离等操作。通过对Treap构建过程的深入剖析,揭示了其背后的数学原理,并提供了一段完整的C++实现代码,有助于读者理解Treap如何高效地进行节点距离查询。

Description

维护一个Treap,支持三种操作

  • 0 k w:插入一个关键字为k,权值为w的点。
  • 1 k:删除一个关键字为k的点。
  • 2 ku kv:返回关键字分别为kukv两个结点的距离。

Solution

这道题寒假就做到了。。一直不会QAQ
考虑Treap是怎么建的。
对所有键值排序后,以权值最大的结点为根,再左右递归建立子树。
所以两个结点的LCA就是两个结点之间权值最大的点。
如何维护一个点到根的距离呢。
一个点向左向右第一个比他大的结点一定是他的祖先,这就成了一个类似于上升序列的东西。
画下图可以发现左右是独立的,分别维护一下就好了。
时间复杂度是O(nlog2n)的。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 201010;
typedef unsigned int ui;

inline char get(void) {
    static char buf[100000], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, 100000, stdin);
        if (S == T) return EOF;
    }
    return *S++;
}
template<typename T>
inline void read(T &x) {
    static char c; x = 0;
    for (c = get(); c < '0' || c > '9'; c = get());
    for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
}

template<typename T>
inline T Max(T a, T b) {
    return a > b ? a : b;
}

struct Seg1 {
    int mx[N << 2];
    int ans[N << 2];
    int l[N << 2], r[N << 2];
    int MX, Ans;

    void Build(int o, int _l, int _r) {
        l[o] = _l; r[o] = _r;
        ans[o] = 0; mx[o] = 0;
        if (_l == _r) return;
        int mid = (_l + _r) >> 1;
        Build(o << 1, _l, mid);
        Build(o << 1 | 1, mid + 1, _r);
    }
    inline int Calc(int o, int v) {
        if (l[o] == r[o]) return mx[o] > v;
        if (v > mx[o << 1]) return Calc(o << 1 | 1, v);
        return Calc(o << 1, v) + ans[o] - ans[o << 1];
    }
    void Modify(int o, int pos, int x) {
        if (l[o] == r[o]) {
            mx[o] = x; ans[o] = 1;
            if (!x) ans[o] = 0;
            return;
        }
        int mid = (l[o] + r[o]) >> 1;
        if (pos <= mid) Modify(o << 1, pos, x);
        else Modify(o << 1 | 1, pos, x);
        mx[o] = Max(mx[o << 1], mx[o << 1 | 1]);
        ans[o] = ans[o << 1] + Calc(o << 1 | 1, mx[o << 1]);
    }
    inline void QAns(int o, int L, int R) {
        if (l[o] >= L && r[o] <= R) {
            Ans += Calc(o, MX);
            MX = Max(MX, mx[o]);
            return;
        }
        int mid = (l[o] + r[o]) >> 1;
        if (R <= mid) return QAns(o << 1, L, R);
        if (L > mid) return QAns(o << 1 | 1, L, R);
        QAns(o << 1, L, R); QAns(o << 1 | 1, L, R);
    }
    inline int QAns(int L, int R) {
        MX = Ans = 0;
        QAns(1, L, R);
        return Ans;
    }
    inline int QMax(int o, int L, int R) {
        if (l[o] >= L && r[o] <= R) return mx[o];
        int mid = (l[o] + r[o]) >> 1, res = 0;
        if (L <= mid) res = Max(res, QMax(o << 1, L, R));
        if (R > mid) res = Max(res, QMax(o << 1 | 1, L, R));
        return res;
    }
    void Init(int n) {
        Build(1, 1, n);
    }
};
Seg1 S1, S2;
int n, opt, x, y, m, q, lca;
struct Opt {
    int opt;
    ui x, y;
};
Opt Q[N];
ui mpy[N], mpx[N];

inline int Dist(int x) {
    int d1 = S1.QAns(x, m), d2 = S2.QAns(m - x + 1, m);
    return d1 + d2;
}

int main(void) {
    freopen("1.in", "r", stdin);
    read(q);
    for (int i = 1; i <= q; i++) {
        read(Q[i].opt); read(Q[i].x);
        if (Q[i].opt != 1) read(Q[i].y);
        if (!Q[i].opt) {
            mpx[++m] = Q[i].x;
            mpy[m] = Q[i].y;
        }   
    }
    sort(mpx + 1, mpx + m + 1);
    sort(mpy + 1, mpy + m + 1);
    for (int i = 1; i <= q; i++)
        if (!Q[i].opt) {
            Q[i].x = lower_bound(mpx + 1, mpx + m + 1, Q[i].x) - mpx;
            Q[i].y = lower_bound(mpy + 1, mpy + m + 1, Q[i].y) - mpy;
        } else {
            Q[i].x = lower_bound(mpx + 1, mpx + m + 1, Q[i].x) - mpx;
            Q[i].y = lower_bound(mpx + 1, mpx + m + 1, Q[i].y) - mpx;
        }
    for (int i = 1; i <= m; i++) mpx[i] = mpy[i] = 0;
    for (int i = 1; i <= q; i++)
        if (!Q[i].opt) mpy[Q[i].y] = Q[i].x;
    S1.Init(m); S2.Init(m);
    for (int i = 1; i <= q; i++) {
        if (Q[i].opt == 0) {
            S1.Modify(1, Q[i].x, Q[i].y);
            S2.Modify(1, m - Q[i].x + 1, Q[i].y);
        } else if (Q[i].opt == 1) {
            S1.Modify(1, Q[i].x, 0);
            S2.Modify(1, m - Q[i].x + 1, 0);
        } else {
            x = Q[i].x; y = Q[i].y;
            if (x > y) swap(x, y);
            int p = S1.QMax(1, x, y);
            lca = mpy[S1.QMax(1, x, y)];
            printf("%d\n", Dist(x) + Dist(y) - 2 * Dist(lca));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值