左偏树+并查集 hdu1512 Monkey King

传送门:点击打开链接

题意:很多只猴子,每个猴子有个强壮值,一开始每只猴子都不认识。会有m次冲突,每次冲突的两只猴子如果不认识,就会各自在认识的人中各找一个最强壮的人打架,打完后打赢的那个人的强壮值会减半,并输出减半后的值,然后猴子两边的人都会互相认识成为朋友(所谓不打不相识..),如果两只猴子冲突但是已经认识了,那么就输出-1

思路:左偏树上次翻论文无意中发现的,感觉这种数据结构好666666。。

论文下载地址:点击打开链接


左偏树的优势就在于非常容易写,而且取最小值的复杂度是O(1),弹出和合并的复杂度是O(logn),因为合并的复杂度非常低,这也使得左偏树的应用可以非常的广泛。

至少在对于许多堆中取最小和最大,并且这些堆会合并的问题中,能很轻易的解决了,那么判断两个是否在同一个堆中,可以利用并查集轻松解决。

这种简单易懂作用十分强大的数据结构很推荐学习,对以后拓宽思路会有非常大的帮助~

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<string>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#define fuck(x) cout<<"["<<x<<"]"
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w+",stdout)
using namespace std;
typedef long long LL;

const int MX = 1e5 + 5;


struct Data {
    int l, r, key, dist;
} D[MX << 2];
int P[MX], rt[MX];
int rear;

int lt_init() {
    rear = 0;
    D[0].dist = -1;
}
int lt_new(int _key = 0) {
    rear++;
    D[rear].l = D[rear].r = 0;
    D[rear].key = _key;
    D[rear].dist = 0;
    return rear;
}
int lt_merge(int r1, int r2) {
    if(!r1) return r2;
    if(!r2) return r1;
    if(D[r1].key < D[r2].key) {
        swap(r1, r2);
    }
    D[r1].r = lt_merge(D[r1].r, r2);
    if(D[D[r1].l].dist < D[D[r1].r].dist) {
        swap(D[r1].l, D[r1].r);
    }
    D[r1].dist = D[D[r1].r].dist + 1;
    return r1;
}
int lt_pop(int &rt) {
    int ret = D[rt].key;
    rt = lt_merge(D[rt].l, D[rt].r);
    return ret;
};
void lt_push(int &rt, int key) {
    rt = lt_merge(rt, lt_new(key));
}
int find(int x) {
    return P[x] == x ? x : (P[x] = find(P[x]));
}

int main() {
    int n, m; //FIN;
    while(~scanf("%d", &n)) {
        lt_init();
        for(int i = 1; i <= n; i++) {
            P[i] = i;
        }
        for(int i = 1; i <= n; i++) {
            int t; scanf("%d", &t);
            rt[i] = lt_new(t);
        }

        scanf("%d", &m);
        for(int i = 1; i <= m; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            int p1 = find(u), p2 = find(v);
            if(p1 == p2) printf("-1\n");
            else {
                rt[p1] = lt_merge(rt[p1], rt[p2]);
                int ans = lt_pop(rt[p1]) / 2;
                lt_push(rt[p1], ans);
                P[p2] = p1;

                printf("%d\n", ans);
            }
        }
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值