洛谷 P3377 【模板】左偏树(可并堆)题解 左偏树模板题

题目链接:https://www.luogu.com.cn/problem/P3377

维护左偏树的同时还需要维护一个并查集。

但是并查集也就一个 find 操作。

pop 的时候更新 f[x] 的操作很神奇。

示例程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;

int n, m, op, x, y, val[maxn], ls[maxn], rs[maxn], f[maxn], dis[maxn];
bool vis[maxn];

// 并查集部分
void init() {
    for (int i = 1; i <= n; i++) f[i] = i;
}
int find(int x) {
    return x == f[x] ? x : f[x] = find(f[x]);
}

// 左偏树部分
void push_up(int x) {
    if (dis[rs[x]] > dis[ls[x]]) swap(rs[x], ls[x]);
    dis[x] = dis[rs[x]] + 1;
}

int merge(int x, int y) {
    if (!x || !y) return x + y;
    if (val[x] > val[y] || val[x] == val[y] && x > y) swap(x, y);
    f[rs[x] = merge(rs[x], y)] = x;
    push_up(x);
    return x;
}

void pop(int x) {
    if (vis[x]) return;
    vis[x] = true;
    f[ls[x]] = ls[x];
    f[rs[x]] = rs[x];
    f[x] = merge(ls[x], rs[x]);
}

int main() {
    scanf("%d%d", &n, &m);
    init();
    for (int i = 1; i <= n; i++)
        scanf("%d", val+i);
    while (m--) {
        scanf("%d%d", &op, &x);
        if (op == 1) {
            scanf("%d", &y);
            if (!vis[x] && !vis[y] && x != y) {
                x = find(x), y = find(y);
                merge(x, y);
            }
        }
        else {
            if (vis[x]) puts("-1");
            else {
                x = find(x);
                printf("%d\n", val[x]);
                pop(x);
            }
        }
    }
    return 0;
}
posted @ 2023-04-04 15:04  quanjun  阅读(36)  评论(0编辑  收藏  举报