7.11 【左偏树】
作者:互联网
\(\large\text{Date: 7.11}\)
左偏树(可并堆)
\(\large\to\text{Link}\leftarrow\)
原理
定义: 若一个节点有儿子是空的,那么这个节点就叫空节点。而一个节点的 \(dis\) 值代表从这个节点出发,只经过右儿子到达一个空节点最少需要走的边数。
左偏树 的意思是:对于一棵树上的每一个节点,其左儿子的 \(dis\) 值大于等于右儿子的 \(dis\) 值,所以这棵树“向左偏”。
若以 \(u,v\) 为根的两个堆需要合并,由于已经维护好了左偏性质,所以只需要将 \(v\) 合并到 \(u\) 最右边的空节点,然后再递归维护左偏性质即可。
很显然,在最坏情况下,由于堆的性质,\(dis[1]\) 到达 \(\log n\),合并操作依然能够维持在 \(\cal O(\log n)\) ,是非常优秀的。
其实这棵树往哪偏都无所谓(
\(\rm Code\) 是最重要的:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n, m, heap[N];
int fa[N], ls[N], rs[N], dis[N];
bool del[N];
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
int Merge(int u, int v) {
if(!u || !v) return u + v; // 为空就返回另一个节点的值
if(heap[u] == heap[v] ? u > v : heap[u] > heap[v]) swap(u, v);
rs[u] = Merge(rs[u], v);
if(dis[ls[u]] < dis[rs[u]]) swap(ls[u], rs[u]);
fa[ls[u]] = fa[rs[u]] = fa[u] = u;
dis[u] = dis[rs[u]] + 1;
return u;
}
void pop(int u) {
del[u] = true;
fa[ls[u]] = ls[u];
fa[rs[u]] = rs[u];
fa[u] = Merge(ls[u], rs[u]);
}
void init() {
for(int i = 1; i <= n; i++) fa[i] = i;
}
signed main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("Ans.txt", "w", stdout);
#endif
dis[0] = -1;
scanf("%d %d", &n, &m);
init();
for(int i = 1; i <= n; i++) scanf("%d", &heap[i]);
for(int opt, x, y; m--; ) {
scanf("%d", &opt);
if(opt == 1) {
scanf("%d %d", &x, &y);
if(del[x] || del[y]) continue;
x = find(x); y = find(y);
if(x != y) fa[x] = fa[y] = Merge(x, y);
}
else {
scanf("%d", &x);
if(del[x]) { puts("-1"); continue; }
x = find(x);
printf("%d\n", heap[x]);
pop(x);
}
}
return 0;
}
这图挺好看
标签:7.11,rs,int,fa,ls,左偏,节点,dis 来源: https://www.cnblogs.com/Doge297778/p/16465370.html