首页 > 其他分享 >[luoguP3377] 左偏树/可并堆

[luoguP3377] 左偏树/可并堆

时间:2024-11-06 21:00:21浏览次数:1  
标签:dist int 合并 tr luoguP3377 节点 左偏

题意

原题链接
给定 \(n\) 个小根堆,初始只有一个元素 \(a_i\),给出 \(m\) 次操作,每次合并堆 \(x,y\) 所在的两个堆或删除 \(x\) 所在的堆顶并输出堆顶

sol

由于堆需要合并,因此需要实现一种合并时间复杂度为 \(O(\log n)\) 的堆数据结构(本题也可 \(O(m\log^2 n)\) 启发式合并),其中一种是左偏树。
左偏树,即向左偏的树,它每个节点的左儿子到最近的外节点的距离一定不小于右儿子到最近的外节点的距离(我们把少于两个儿子的节点叫做外节点),容易得到,根节点到最近的外节点的距离等于右儿子到最近的外节点的距离加一。
在合并时,将权值更小的点作为根,将它的右儿子和另一个堆递归合并,合并后若不满足左偏树性质,则交换左右儿子。
插入节点时,只需要将一个节点的堆与其合并即可;删除根节点时,只需要合并左右儿子即可。
注意本题需要并查集来维护所在的堆。

代码

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 100005;

struct Node{
    int val;
    int l = 0, r = 0;
    int dist = 0;
}tr[N];

bool st[N];
int fa[N];
int n, m;

int find(int x){
    if (fa[x] == x) return x;
    return fa[x] =  find(fa[x]);
}

int merge(int x, int y){
    if (!x || !y) return x | y;
    if (tr[x].val > tr[y].val) swap(x, y);
    tr[x].r = merge(tr[x].r, y);
    if (tr[tr[x].l].dist < tr[tr[x].r].dist) swap(tr[x].l, tr[x].r);
    tr[x].dist = tr[tr[x].r].dist + 1;
    return x;
}

int main(){
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &tr[i].val), fa[i] = i;
    while (m -- ){
        int op;
        scanf("%d", &op);
        if (op == 1){
            int x, y;
            scanf("%d%d", &x, &y);
            int fx = find(x), fy = find(y);
            if (fx == fy || st[x] || st[y]) continue;
            fa[fx] = fa[fy] = merge(fx, fy);
        }
        else {
            int x;
            scanf("%d", &x);
            int fx = find(x);
            if (st[x]) puts("-1");
            else {
                printf("%d\n", tr[fx].val);
                st[fx] = true;
                fa[fx] = fa[tr[fx].l] = fa[tr[fx].r] = merge(tr[fx].l, tr[fx].r);
                tr[fx].l = tr[fx].r = tr[fx].dist = 0;
            }
        }
    }
}

标签:dist,int,合并,tr,luoguP3377,节点,左偏
From: https://www.cnblogs.com/XiaoJuRuoUP/p/-/luoguP3377

相关文章

  • 左偏树
    具体见OI-wiki,但是OI-wiki对左偏树的“外节点”的定义好像错了,其实应该就是指空节点;删除任意一个数的那个部分就不用看了,没啥用设\(f(k)\)表示\(\text{dist}\)为\(k\)的左偏树最少包含的点,则有\(f(k)≥2^k-1\)证明:\(f(k)\)单调递增,这是因为此时右子树的\(\text{dist}\)肯定为\(k......
  • 左偏树(可并堆)
    左偏树(可并堆)定义在这之前,我们先来阐述一些定义:外节点:\(ls\)或\(rs\)为空的节点距离:节点的距离\(dist_x\)定义为节点\(x\)到距\(x\)最近的外节点的距离,空节点的距离为\(-1\)其次是左偏树的性质:左偏性:即满足\(dist_{ls}>=dist_{rs}\)堆性质:若满足小根堆,则满......
  • 左偏树/可并堆
         1.什么是左偏树? 上面的树都是左偏树。先引出一个概念,dis等于节点到它子树里面最近的叶子节点的距离,特别地叶子节点的dis等于0。观察上图我们可以感性理解左偏树,就是左子树的深度大于等于右子树,看上去整个树向左偏。再看一眼就可以总结出几条性质:1.左儿子的......
  • 左偏树
    前言左偏树是一种可并堆,顾名思义,它支持快速合并。定义定义外界点为孩子数量小于等于\(2\)个的节点,\(dis(u)\)表示节点\(u\)到最近的外节点经过的边数减\(1\)。特别的,空节点的\(dis\)为\(-1\)。定义节点\(u\)权值为\(val(u)\),左、右儿子分别为\(ls(u),rs(u)\)。左......
  • 【博客】左偏树
    左偏树前言左偏树是一棵向左偏的树左偏树是一种能在\(O(\logn)\)之内完成合并的可并堆长这样我们常用左偏树完成以下操作在指定集合中插入一个元素查询集合中最高优先级的元素删除集合中最高优先级的元素删除指定元素合并两个集合性质首先我们要知道左偏树的......
  • 左偏树
    左偏树是一种可并堆(一系列的堆),支持以下操作:删除一个堆的最值。查询一个堆的最值。新建一个堆,只包含一个元素。合并两个堆。这个复杂度是\(O(\log)\)的。左偏树是一颗二叉树。定义“外结点”为儿子数量不等于\(2\)的结点,定义每个结点的\(dist\)为该结点到最......
  • 左偏树/可合并堆
    左偏树/可合并堆代码笔记代码思路主体部分:合并堆(即merge函数)大堆左偏,把小堆和大堆的右儿子合并。感性理解:堆的形态将比较平衡。辅助部分:并查集维护堆关系简化部分:自定义数据类型(structBheap)注意事项:堆的最大数量是\(n+m\)注意考虑堆被删空等细节情况(尤其是题目......
  • 左偏树/可并堆
    20231107左偏树/可并堆将左偏树/可并堆做一个小结,不写我可能就要忘了。。。左偏树,顾名思义,就是保证左子树深度一定大于右子树,同时需要满足堆的性质,于是在合并两个堆的时候的时间复杂度就为\(\logn\),感觉是非常易懂的,具体实现的细节还是有一些。注意我们会用到并查集和......
  • 数据结构——左偏树/可并堆学习笔记
    引入作为树形数据结构的一员——堆,对于取极值拥有着优秀的复杂度,但是,合并两个堆却成为了一个问题。除了朴素算法外,还有什么算法可以合并两个堆呢?正文那么,可并堆是个啥呢?简单来说,它是一个支持合并操作的二叉堆(好像是废话)。首先,简单介绍一下二叉堆的性质,学过的读者可自行跳过。......
  • 【学习笔记】左偏树
    左偏树属于可并堆的一种,可并堆,也就是可以在较低的时间复杂度下完成对两个堆的合并。定义及性质对于一棵二叉树,定义外节点为左儿子或右耳子为空的节点,定义其的\(dist\)为\(1\),而不是外节点的\(dist\)为其到子树中最近的外节点距离\(+1\)。空节点的\(dist\)为\(0\)。例......