BZOJ1455 罗马游戏 【左偏树】

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1455

题解:
本题显然可以用堆来实现,(维护一个大根堆),但是无法进行合并操作,于是我们想到左偏树。

定义一个结点的斜深度为这个节点不断向自己的右儿子走 直到为叶子节点的长度。左偏树的“左偏”指左儿子的斜深度一定大于等于右儿子的斜深度。合并就简单了,我们可以归并的来维护一个左偏树,设需合并的两个树的根节点为 k1 k2,那么我们可以先将以 k1 为根节点的右子树和 k2 那个子树合并,再不断调整即可。
PS:数组一定要开大,重要的事情说一遍!我因此RE 4次!

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map> // STL
#include <string> 
#include <vector>
#include <queue>
#include <stack>
#define mpr make_pair
#define debug() puts("okkkkkkkk")

using namespace std;

typedef long long LL;

const int inf = 1 << 26;

struct trees {
    int l, r, w, size;
} t[1000005];

int n, Q;
int fa[1000005], die[1000005];

int find(int x) { 
    if(fa[x] == x) return fa[x];
    else return fa[x] = find(fa[x]); 
}

int merge(int k1, int k2) {     // 合并以 k1 k2 为根节点的左偏树,返回合并后树的根 
    if(!k1 || !k2) return k1+k2;
    if(t[k1].w > t[k2].w) swap(k1, k2);
    t[k1].r = merge(t[k1].r, k2);
    if(t[t[k1].r].size > t[t[k1].l].size) swap(t[k1].r, t[k1].l);
    t[k1].size = t[t[k1].l].size + t[t[k1].r].size + 1;

    return k1;
} 

int main(){
    scanf("%d", &n);
    for ( int i = 1; i <= n; i ++ ) scanf("%d", &t[i].w);
    for ( int i = 1; i <= n; i ++ ) fa[i] = i;
    scanf("%d", &Q);
    while( Q -- ) {
        char ch[25];
        scanf("%s", ch);
        if(ch[0] == 'M') {
            int x, y;
            scanf("%d %d", &x, &y);
            if(die[x] || die[y]) continue;
            int fx = find(x), fy = find(y);
            if(fx != fy) {
                int temp = merge(fx, fy);
                fa[fx] = fa[fy] = temp;
            }
        } else if(ch[0] == 'K') {
            int x;
            scanf("%d", &x);
            if(die[x]) { puts("0"); continue; }

            int temp = find(x); die[temp] = 1;
            printf("%d\n", t[temp].w);
            fa[temp] = merge(t[temp].l, t[temp].r);
            fa[fa[temp]] = fa[temp];
        }
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值