左偏树
在做一道题的时候遇到了就顺手学了
刚开始打的线段树合并,结果空间直接炸裂,然后尝试玄学得了60分
后面看标签才知道要用左偏树.
左偏树满足下面几个性质
1.根节点的 \(val\) 小于儿子节点的 \(val\)
2.左儿子到叶子结点的 \(dis\) 大于等于右儿子的 \(dis\)
3.根节点的 \(dis\) 等于右儿子的 \(dis + 1\)
然后是左偏树的一些操作
合并操作
int merge(int x, int y)
{
if (!x || !y)
return x + y;
if (tr[x].v > tr[y].v)
swap(x, y);
rs = merge(rs, y);
if (tr[ls].dis < tr[rs].dis)
swap(ls, rs);
tr[ls].rt = tr[rs].rt = tr[x].rt = x;
tr[x].dis = tr[rs].dis + 1;
return x;
}
让 \(x\) 作为根节点, \(y\) 连到 \(x\) 的右儿子上面,如果右儿子的 \(dis\)更大,交换左右儿子
删除操作
void pop(int x)
{
tr[x].v = -1;
tr[ls].rt = ls, tr[rs].rt = rs;
tr[x].rt = merge(ls, rs);
}
删除根节点
获取根节点
int get(int x)
{
return tr[x].rt == x ? x : tr[x].rt = get(tr[x].rt);
}
然后是完整代码
#include<iostream>
#include<cstdio>
#define ls tr[x].l
#define rs tr[x].r
using namespace std;
const int N = 1e6 + 5;
struct tree
{
int l, r, rt, v, dis;
} tr[N];
int merge(int x, int y)
{
if (!x || !y)
return x + y;
if (tr[x].v > tr[y].v)
swap(x, y);
rs = merge(rs, y);
if (tr[ls].dis < tr[rs].dis)
swap(ls, rs);
tr[ls].rt = tr[rs].rt = tr[x].rt = x;
tr[x].dis = tr[rs].dis + 1;
return x;
}
int get(int x)
{
return tr[x].rt == x ? x : tr[x].rt = get(tr[x].rt);
}
void pop(int x)
{
tr[x].v = -1;
tr[ls].rt = ls, tr[rs].rt = rs;
tr[x].rt = merge(ls, rs);
}
int main()
{
int n, m;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &tr[i].v), tr[i].rt = i;
scanf("%d", &m);
while (m--)
{
char opt;
int x, y;
scanf(" %c", &opt);
if (opt == 'M')
{
scanf("%d%d", &x, &y);
if (tr[x].v == -1 || tr[y].v == -1)
continue;
int r1 = get(x), r2 = get(y);
if (r1 != r2)
tr[r1].rt = tr[r2].rt= merge(r1, r2);
}
else
{
scanf("%d", &x);
if (tr[x].v == -1)
puts("0");
else
printf("%d\n", tr[get(x)].v), pop(get(x));
}
}
return 0;
}