zoukankan      html  css  js  c++  java
  • 可并堆讲解

    可并堆讲解

      注:本篇博客以大根堆为例。

      可并堆,顾名思义,就是可以合并的堆。堆满足一个性质,就是当前节点,都大于或者等于他的所有子树上的节点,自然在这里我所讲的是结点的权值。显而易见,既然可并堆是堆的一种,容易推出,可并堆也满足这个性质。

      现在思考一个问题,当题目里需要合并两个堆的时候,该如何合并呢?如果只是普通的堆的话,我们可以运用启发式合并的思想,每一次把size小的堆的点暴力插入到size大的堆里面,这个方法十分不优秀,时间复杂度是O(nlog2n)。所以我们会用可并堆来实现这个问题。

      可并堆是一种运用到左偏树思想的堆,何为左偏树?左偏树顾名思义就是偏向左面的树,如左图就是一棵左偏树,这棵树明显满足一个性质。我们定义dis[p]为从p节点出发可以向右走的最大步数,从右图中可以看到,当前点的左儿子的dis值都比右儿子的dis值大,只要满足这个性质就是左偏树。

      学过左偏树之后,我们就可以学习可并堆了,可并堆的难点就是合并,那么应该如何合并呢?为了能保证时间复杂度,我们每一次合并都应该把新的节点放在当前的节点的右儿子上,这个是一个递归的过程,每一次把当前的两个节点进行比较,留下权值大的点,然后递归下去把另一个点和留下的点的右儿子进行比较,如此下去,进行合并。当然每一次回溯之前,我们都需要判断当前节点的左右两个儿子的dis值,如果右儿子的dis值大于左儿子的dis值,则交换左右儿子。

    int merge(int x,int y)
    {
        if(!x) return y;
        if(!y) return x;
        if(num[x]<num[y]) swap(x,y);
        son[x][1]=merge(son[x][1],y);
        if(dis[son[x][1]]>dis[son[x][0]])
            swap(son[x][1],son[x][0]);
        dis[x]=dis[son[x][1]]+1;
        return x;
    }
    //son[p][0]表示p号节点的左儿子
    //son[p][1]表示p号节点的右儿子
    //num[p]表示p号节点的权值

      下面就是删除节点(最大值),只需要把他的两个儿子合并就好啦,是不是很简单?代码就不附了,具体题目具体分析。

      大致就是这样,不会的可以评论发问题,我会解答。

    题目(我会不断更新)

      bzoj1455&&luogu2713罗马游戏:http://www.cnblogs.com/yangsongyi/p/8893005.html

      APIO2012派遣:http://www.cnblogs.com/yangsongyi/p/8921448.html

      JLOI2015城池攻占:http://www.cnblogs.com/yangsongyi/p/9046913.html

  • 相关阅读:
    HDU 2236 无题II
    P2220 [HAOI2012]容易题
    UVA11383 Golden Tiger Claw
    AT2272 [ARC066B] Xor Sum
    CentOS7 静默安装oracle12c
    SNAT与DNAT
    Linux下离线安装Docker
    TJOI2017 DNA 和 BJOI2015 隐身术
    LOJ6169 相似序列
    BJOI2019 删数
  • 原文地址:https://www.cnblogs.com/yangsongyi/p/8892222.html
Copyright © 2011-2022 走看看