hdu1512 & zoj2334Monkey King(左偏树 + 并查集)

参考:http://blog.csdn.net/pi9nc/article/details/11827501
题目:https://vjudge.net/problem/HDU-1512

他的注释很详细.

题目大意:有n个猴子,一开始每个猴子只认识自己。每个猴子有一个力量值,力量值越大表示这个猴子打架越厉害。如果2个猴子不认识,他们就会找他们认识的猴子中力量最大的出来单挑,单挑不论输赢,单挑的2个猴子力量值减半,这2拨猴子就都认识了,不打不相识嘛。现在给m组询问,如果2只猴子相互认识,输出-1,否则他们各自找自己认识的最牛叉的猴子单挑,求挑完后这拨猴子力量最大值。
#include<bits/stdc++.h>

using namespace std;
#define MP make_pair
#define N 100005
#define LL long long
#define pb push_back
#define fi first
#define se second
#define pii pair<int, int>
#define md ((ll+rr)>>1)
#define ls (i<<1)
#define rs ((i<<1)|1)
#define lson ls,ll,md
#define rson rs,md+1,rr


int n,m;
int fa[N];
int a[N];
int l[N],r[N],v[N],d[N];
int Find(int x){
    return x==fa[x]?x:fa[x]=Find(fa[x]);
}

int merge(int x,int y){
    if(!x)return y;
    if(!y)return x;
    if(v[x]<v[y])swap(x,y);//大根堆
    r[x]=merge(r[x],y);
    fa[r[x]]=x;//并查集的部分
    if(d[l[x]]<d[r[x]])swap(l[x],r[x]);
    d[x]=d[r[x]]+1;
    return x;
}
int pop(int x){
    fa[l[x]]=l[x];fa[r[x]]=r[x];//先把左儿子设自己为跟,右儿子也是
    int ll=l[x],rr=r[x];
    l[x]=r[x]=d[x]=0;
    //printf("%d %d\n",ll,rr);
    return merge(ll,rr);
}
int tot;
void Init(int x){
    tot++;
    v[tot]=x;
    l[tot]=r[tot]=d[tot]=0;
}
int main(){
    while(~scanf("%d",&n)){
        tot=0;
        for(int i=1;i<=n;++i){
            scanf("%d",&a[i]);
            fa[i]=i;
            Init(a[i]);
        }
        scanf("%d",&m);
        for(int i=1;i<=m;++i){
            int x,y;scanf("%d%d",&x,&y);
            int fx=Find(x),fy=Find(y);
            if(fx==fy)puts("-1");
            else{
                int tmpx=pop(fx);//先把最小的取出来
                v[fx]/=2;
                fx=merge(tmpx,fx);//再合并回去.找到合适的位置插入,这样就不会违反堆 的性质
                int tmpy=pop(fy);
                v[fy]/=2;
                fy=merge(tmpy,fy);
                printf("%d\n",v[merge(fx,fy)]);
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值