平衡树
前言
在此,我们总结一下平衡树的各种操作,可能不全,但会实时更新
Part.1 基础模板
这一部分是基本上不会变化的操作,也是每个平衡树的题目必写的模板。
No.1 旋转操作
void rotate(int x)
{
int y = tr[x].fa;
int z = tr[y].fa;
int k = (tr[y].son[1] == x);
tr[z].son[(tr[z].son[1] == y)] = x, tr[x].fa = z;
tr[y].son[k] = tr[x].son[k ^ 1], tr[tr[x].son[k ^ 1]].fa = y;
tr[x].son[k ^ 1] = y, tr[y].fa = x;
return;
}
No.2 伸展操作
void splay(int x, int k)
{
while (tr[x].fa != k)
{
int y = tr[x].fa;
int z = tr[y].fa;
if (z != k)
if ((tr[y].son[1] == x) != (tr[z].son[1] == y))
rotate(x);
else rotate(y);
rotate(x);
}
if (!k) root = x;
return;
}
No.3 树的存储
其实这里具体题目会有具体的存储方式,但是对于每一道题目必须要存储的信息如下
struct warma
{
int son[2], fa, val;
void init(int _v, int _p)
{
val = _v;
fa = _p;
}
} tr[N];
可能有些题目需要子树大小,那么我们再将其加入一个变量,如下
struct warma
{
int son[2], fa, val;
int sz;
void init(int v, int p)
{
val = v;
fa = p;
sz = 1;
}
} tr[N];
可能有些毒瘤题,维护的数据更多,如 P2042 [NOI2005] 维护数列 所需存储的信息如下
struct warma
{
int son[2];
int fa, val;
bool rev, same;
int sz, sum, ms, ls, rs;
void init(int _v, int _p)
{
son[0] = son[1] = 0;
fa = _p, val = _v;
rev = same = 0;
sz = 1;
sum = ms = val;
ls = rs = max(val, 0);
}
} tr[N];
Part.2 操作模板
这一部分变化性就大了,不过其实你全背过也应该没问题
No. 1 加点操作
这个分为两种情况,如果你的平衡树里维护的值需要保证左儿子的值<=该节点的值<=右儿子的值的话,应该用如下方式加点
void add(int v)
{
int u = root, p = 0;
while (u)
{
p = u;
u = tr[u].son[v > tr[u].val];
}
u = ++ idx;
if (p) tr[p].son[v > tr[p].val] = u;
tr[u].init(v, p);
splay(u, 0);
return;
}
如果你的平衡树只需要满足中序遍历顺序不变的话,那么应该这样加点(方式改编自P2596 [ZJOI2006]书架)
void add(int v)
{
int u = root;
while (tr[u].son[1]) u = tr[u].son[1];
tr[ ++ idx].init(v, u);
tr[u].son[1] = idx;
pos[v] = idx;
splay(idx, 0);
return;
}
No.2 删除操作
删除值为 x 的节点(x 已知)
void del(int x)
{
int a = get_pre(x);
int b = get_sub(x);
splay(a, 0);
splay(b, a);
tr[b].son[0] = 0;
return;
}
Part.3 询问操作
我们令性质 A 为:平衡树中满足左儿子的值<=该节点的值<=右儿子的值。
No. 1 排名相关
满足 A,询问第 \(k\) 小的值的点的编号
该操作改编自P3224 [HNOI2012]永无乡
int get_k(int k)
{
int u = root;
while (u)
{
if (tr[tr[u].son[0]].sz >= k) u = tr[u].son[0];
else if (tr[tr[u].son[0]].sz + 1 == k) return u;
else
{
k -= tr[tr[u].son[0]].sz + 1;
u = tr[u].son[1];
}
}
return -1;
}
满足 A,询问排名为 k 的值为多少(k 已知)
int get_num_by_rank(int k)
{
int u = root;
while (u)
{
if (tr[tr[u].son[0]].sz >= k) u = tr[u].son[0];
else if (tr[tr[u].son[0]].sz + 1 == k) return tr[u].val;
else
{
k -= tr[tr[u].son[0]].sz + 1;
u = tr[u].son[1];
}
}
return -1;
}
No. 2 值->点的编号
void fd(int v) //查找值为x的点
{
int u = root;
while (tr[u].val != v)
{
if (tr[u].val > v)
{
if (!tr[u].son[0]) break;
u = tr[u].son[0];
}
else
{
if (!tr[u].son[1]) break;
u = tr[u].son[1];
}
}
splay(u, 0);
}
No.3 前驱后继
1. 满足 A,查找值为 v 的点的前驱节点的编号(v 已知且存在)
int get_pre(int v)
{
fd(v);
if (tr[root].val < v) return root;
int u = tr[root].son[0];
while (tr[u].son[1])
u = tr[u].son[1];
return u;
}
2. 满足 A,查找值为 v 的点的后继节点的编号(v 已知且存在)
int get_sub(int v)
{
fd(v);
if (tr[root].val > v) return root;
int u = tr[root].son[1];
while (tr[u].son[0])
u = tr[u].son[0];
return u;
}
3. 满足 A,找到小于 v 的最大数(v 已知但可以不存在)
int get_pre(int v) //找小于v的最大数
{
int u = root;
int res = -INF;
while (u)
{
if (tr[u].val < v)
{
res = max(res, tr[u].val);
u = tr[u].son[1];
}
else u = tr[u].son[0];
}
return res;
}
4. 满足 A,找到大于 v 的最小数(v 已知但可以不存在)
int get_suc(int v) //找大于v的最小数
{
int u = root;
int res = INF;
while (u)
{
if (tr[u].val > v)
{
res = min(res, tr[u].val);
u = tr[u].son[0];
}
else u = tr[u].son[1];
}
return res;
}

平衡树总结
浙公网安备 33010602011771号