HDU 1512 Monkey King(左偏树+并查集)

【题目链接】 http://acm.hdu.edu.cn/showproblem.php?pid=1512

 

【题目大意】

  现在有 一群互不认识的猴子,每个猴子有一个能力值,每次选择两个猴子,挑出他们所归属的部落中能力值最强的猴子打架,然后两个最强的猴子能力值减半,之后两个部落就合为一个部落,问每次合并后部落中最强的猴子能力值是多少

【题解】

  要求每次取出一堆数字中最大的数字减半再放回去,显然这是优先队列可以完成的操作,但由于之后要将两堆数字合并,所以采用可并优先队列,考虑使用左偏树。在左偏树的维护中同时用并查集维护集合标记的更改,即可以满足题目中所有的要求。

【代码】

#include <cstdio>
#include <algorithm> 
using namespace std;
const int N=100005;
int n,m,f[N],x,y;
struct Node{
    int l,r,d,v;
    Node(){}
    Node(int _l,int _r,int _d,int _v){l=_l,r=_r,d=_d,v=_v;}
}T[N];
int merge(int a,int b){
    if(!a)return b;if(!b)return a;
    if(T[a].v<T[b].v)swap(a,b);
    T[a].r=merge(T[a].r,b);
    f[T[a].r]=a;
    if(T[T[a].l].d<T[T[a].r].d)swap(T[a].l,T[a].r);
    T[a].d=T[a].r?T[T[a].r].d+1:0;
    return a;
}
int pop(int a){
    int l=T[a].l,r=T[a].r;
    f[T[a].l]=T[a].l;
    f[T[a].r]=T[a].r;
    T[a].l=T[a].r=T[a].d=0;
    return merge(l,r);
}
int sf(int x){return x==f[x]?x:f[x]=sf(f[x]);}
int main(){
    while(~scanf("%d
",&n)){
        for(int i=1;i<=n;i++){
            scanf("%d",&x);
            T[i]=Node(0,0,0,x);
            f[i]=i;
        }scanf("%d",&m);
        while(m--){
            scanf("%d%d",&x,&y);
            x=sf(x),y=sf(y);
            if(x==y)puts("-1");
            else{
                int tx=pop(x),ty=pop(y);
                T[x].v/=2; x=merge(x,tx);
                T[y].v/=2; y=merge(y,ty);
                printf("%d
",T[merge(x,y)].v);
            }
        }
    }return 0;
} 
原文地址:https://www.cnblogs.com/forever97/p/hdu1512.html