左偏树的形态是向左偏的,每个节点维护左右孩子,权值,父亲和它到最远叶子的距离。
①:一开始所有点都是孤立的。
②:合并两个节点所在的堆? 一开始要找到这两个堆的根节点。然后从根往下走。假设A的权值小于B(否则就交换AB),那么只要合并A的右子树和B,把合并后的结果作为A的右子树。 递归引用操作。如果右孩子的深度大于左孩子了,就交换左右孩子。
③:删除一个节点所在的堆的最小值?直接走到根,输出来,再把左右子树合并就好了。
注意!
这里的向上找根是要用并查集进行路径压缩的。这就导致了以后getfa的时候可能会连接到0。。 tr[x].fa = merge(tr[x].ls, tr[x].rs). 这样的话就可以让getfa的时候强制连回去,就不错了。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
struct lftree {
int ls, rs, v, d, fa;
}tr[N];
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-')f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
int n, m;
inline int merge(int p, int q) {
if (!p || !q) return p + q;
if (tr[p].v > tr[q].v || (tr[p].v == tr[q].v && p > q)) swap(p, q);
int &rs = tr[p].rs, &ls = tr[p].ls;
rs = merge(rs, q);
tr[rs].fa = p;
if (tr[rs].d > tr[ls].d) swap(ls, rs);
tr[p].d = tr[rs].d + 1;
return p;
}
inline int getf(int x) {return tr[x].fa == x ? x : tr[x].fa = getf(tr[x].fa);}
inline void del(int p) {
tr[p].v = -1;
tr[tr[p].ls].fa = tr[p].ls;
tr[tr[p].rs].fa = tr[p].rs;
tr[p].fa = merge(tr[p].ls, tr[p].rs); // 注意这里呀!
}
int main() {
n = read(); m = read();
for (int i = 1; i <= n; ++i) tr[i].fa = i, tr[i].v = read();
while (m--) {
int opt = read();
if (opt == 1) {
int x = read(), y = read();
if (tr[x].v == -1 || tr[y].v == -1) continue;
int p = getf(x), q = getf(y);
if (p != q) merge(p, q);
}
else {
int x = read();
if (tr[x].v == -1) {puts("-1"); continue;}
int p = getf(x);
printf("%d\n", tr[p].v);
del(p);
}
}
return 0;
}