其他分享
首页 > 其他分享> > 7.11 【左偏树】

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;
}

这图挺好看

\(\rm Link\)

标签:7.11,rs,int,fa,ls,左偏,节点,dis
来源: https://www.cnblogs.com/Doge297778/p/16465370.html