Description
维护一个Treap,支持三种操作
- 0 k w:插入一个关键字为k,权值为
w 的点。 - 1 k:删除一个关键字为k的点。
2 ku kv :返回关键字分别为ku和kv两个结点的距离。
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;
}

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

被折叠的 条评论
为什么被折叠?



