洛谷 P3377 【模板】左偏树(可并堆) 左偏树

https://www.luogu.com.cn/problem/P3377
题目描述
如题,一开始有N个小根堆,每个堆包含且仅包含一个数。接下来需要支持两种操作:

操作1: 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或第y个数已经被删除或第x和第y个数在用一个堆内,则无视此操作)

操作2: 2 x 输出第x个数所在的堆最小数,并将其删除(若第x个数已经被删除,则输出-1并无视删除操作)

输入格式
第一行包含两个正整数N、M,分别表示一开始小根堆的个数和接下来操作的个数。

第二行包含N个正整数,其中第i个正整数表示第i个小根堆初始时包含且仅包含的数。

接下来M行每行2个或3个正整数,表示一条操作,格式如下:

操作1 : 1 x y

操作2 : 2 x

输出格式
输出包含若干行整数,分别依次对应每一个操作2所得的结果。

输入输出样例
输入 #1复制
5 5
1 5 4 2 3
1 1 5
1 2 5
2 2
1 4 2
2 2
输出 #1复制
1
2
说明/提示
当堆里有多个最小值时,优先删除原序列的靠前的,否则会影响后续操作1导致WA。

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=10,M<=10

对于70%的数据:N<=1000,M<=1000

对于100%的数据:N<=100000,M<=100000

思路:参见https://www.cnblogs.com/pks-t/p/10326682.html

#include<bits/stdc++.h>
#define ls tree[x].son[0]
#define rs tree[x].son[1]
using namespace std;
typedef long long ll;

const int maxn=1e5+5;

struct Tree
{
    int dis,val,son[2],rt;
}tree[maxn];

int n,m;

int Merge(int x,int y)
{
    if(!x||!y)
        return x+y;
    if(tree[x].val>tree[y].val||(tree[x].val==tree[y].val&&x>y))
        swap(x,y);
    rs=Merge(rs,y);
    if(tree[rs].dis>tree[ls].val)
        swap(ls,rs);
    tree[ls].rt=tree[rs].rt=tree[x].rt=x;
    tree[x].dis=tree[rs].dis+1;
    return x;
}

int father(int x)
{
    return tree[x].rt==x?x:tree[x].rt=father(tree[x].rt);
}

void Delete(int x)
{
    tree[x].val=-1;
    tree[ls].rt=ls;
    tree[rs].rt=rs;
    tree[x].rt=Merge(ls,rs);
}

int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        tree[i].rt=i;
        scanf("%d",&tree[i].val);
    }
    int op,u,v;
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&op);
        if(op==1)
        {
            scanf("%d %d",&u,&v);
            if(tree[u].val==-1||tree[v].val==-1)
                continue;
            int fu=father(u),fv=father(v);
            if(fu==fv)
                continue;
            tree[fu].rt=tree[fv].rt=Merge(fu,fv);
        }
        else
        {
            scanf("%d",&u);
            if(tree[u].val==-1)
                printf("-1\n");
            else
            {
                int fu=father(u);
                printf("%d\n",tree[fu].val);
                Delete(fu);
            }
        }
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值