hdu1512 Monkey King【左偏堆、并查集】

今天临“放学”看了好久左偏堆未果,直到下载了一个课件==

我们定义节点i为外节点,当且仅当i的左子树或右子树为空。
  一个节点的距离为他到他后代中,最近的外节点所经过的边数。特别的,如果一个节点本身为外节点,则这个节点的距离为0。一棵子树的距离为这棵子树根节点的距离。为了方便,空节点的距离为-1。
  左偏树的左偏性质即左偏树的每个节点左子节点的距离都不小于右子节点的距离。

  即:dis(i,l)>=dis(i.r)

合并两个堆的伪代码

Function Merge(A,B)
  Begin
    If A=Null Then Return(B)
    If B=Null Then Return(A)
    If key(A)>key(B) then Swap(A,B)
    A.Right ← Merge(A.Right,B)
    If A.Right.dis>A.Left.dis Then
        Swap(A.Right,A.Left)
    If A.Right=Null Then A.dis ← 0
                    Else A.dis ← A.Right.dis+1
    Return(A)
  End

说明见下面的代码,然后还有一点就是,我们能看出来这是一个递归的函数,为什么做了递归呢?它的实现是把有着较小(因为要建大顶堆)的树作为另一颗树的一个分支插进去,那个树原来的分支作为新的树继续插进去,直到这个树折腾没,插入的过程结束了。然而还需要从下往上递归维护左偏堆的性质

代码不是我的,太困了==基本也是裸的题,没啥好写的

#include <iostream>
#include <vector>
using namespace std;

const int maxn = 100005;


struct tree {
    int l, r, v, dis, f;
}heap[maxn];


int merge( int a, int b ) {
    if( a == 0 ) return b;
    if( b == 0 ) return a;
    if( heap[a].v < heap[b].v ) swap( a, b );
    heap[a].r = merge( heap[a].r, b );
    heap[heap[a].r].f = a;
    if( heap[heap[a].l].dis < heap[heap[a].r].dis ) swap( heap[a].l, heap[a].r );
    if( heap[a].r == 0 ) heap[a].dis = 0;
    else heap[a].dis = heap[heap[a].r].dis + 1;
    return a;
}


int pop( int a ) {
    int l = heap[a].l;
    int r = heap[a].r;
    heap[l].f = l;
    heap[r].f = r;
    heap[a].l = heap[a].r = heap[a].dis = 0;
    return merge(l, r);
}


int find( int a ) { return heap[a].f == a ? a : find( heap[a].f ) ; }

void Read( int &x ) {
    char ch;
    x = 0;
    ch = getchar();
    while( !(ch >= '0' && ch <= '9') ) ch = getchar();
    while( ch >= '0' && ch <= '9' ) {
        x = x * 10 + ch - '0' ;
        ch = getchar();
    }
}


int main() {
//  freopen( "c:/aaa.txt", "r", stdin );
    int i, a, b, finda, findb, n, m;
    while( scanf( "%d", &n ) == 1 ) {
        for( i=1; i<=n; ++i ) {
            Read(heap[i].v);
            //scanf( "%d", &st[i].v );
            heap[i].l = heap[i].r = heap[i].dis = 0;
            heap[i].f = i;
        }

        //scanf( "%d", &m );
        Read( m );
        while( m-- ) {
            //scanf( "%d %d", &a, &b );
            Read( a ); Read( b );
            finda = find( a );
            findb = find( b );
            if( finda == findb ) {
                printf("-1\n");
            } else {
                heap[finda].v /= 2;
                int u = pop( finda );
                u = merge( u, finda );

                heap[findb].v /= 2;
                int v = pop( findb );
                v = merge( v, findb );

                printf( "%d\n", heap[merge( u, v )].v );
            }
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值