[数据结构] 配对堆

模板链接:https://www.luogu.org/problemnew/show/P3371

【模板】堆

题目描述

如题,初始小根堆为空,我们需要支持以下3种操作:

操作1: 1 x 表示将x插入到堆中

操作2: 2 输出该小根堆内的最小数

操作3: 3 删除该小根堆内的最小数

输入输出格式
输入格式:

第一行包含一个整数N,表示操作的个数

接下来N行,每行包含1个或2个正整数,表示三种操作,格式如下:

操作1: 1 x

操作2: 2

操作3: 3

输出格式:

包含若干行正整数,每行依次对应一个操作2的结果。

输入输出样例
输入样例#1:

5
1 2
1 5
2
3
2

输出样例#1:

2
5

说明

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=15

对于70%的数据:N<=10000

对于100%的数据:N<=1000000(注意是6个0。。。不过不要害怕,经过编者实测,堆是可以AC的)

样例说明:

故输出为2、5

题解

某位毒瘤张瀚文老师的福,学习了一大堆堆知识。

配对堆不像其他娇气的堆,它对堆本身的形态没有什么要求,只要是棵树就行了,插入/合并也很任性,直接比较两个根的大小,优先级高的当爸爸,轻松 O(1) O ( 1 ) 完成插入/合并。

配对堆还可以支持改变堆中元素的值,只需要将元素原来的父边断掉,修改完权值后直接与根合并就完了,虽然看起来是 O(1) O ( 1 ) 的,但是因为会影响到 pop p o p 操作,所以均摊下来复杂度为 O(log2n) O ( l o g 2 n )

因为前面的操作比较随意,所以 pop p o p 操作要收拾随意连边的烂摊子,我们要把根节点的所有儿子不停地两两合并合成一棵树,复杂度是 O() O ( 儿 子 个 数 ) 的,但是神奇摊还分析说,它是 O(log2n) O ( l o g 2 n ) 的,它就是 O(log2n) O ( l o g 2 n ) 的。

代码

贼好写,甚至短于左偏树。

#include<bits/stdc++.h>
using namespace std;
const int M=2e6+5;
int n,root,id,tot,top,val[M],head[M],nxt[M],to[M],dad[M];
queue<int>dui;
void add(int x,int y){nxt[++id]=head[x],head[x]=id,to[id]=y,dad[y]=x;}
int _new(int x){val[++tot]=x;return tot;}
int merge(int x,int y){if(!x||!y)return x+y;if(val[x]<val[y]){add(x,y);return x;}else {add(y,x);return y;}}
void pop()
{
    int t;
    for(int i=head[root];i;i=nxt[i])if(dad[to[i]]==root)dui.push(to[i]),dad[to[i]]=0;
    while(dui.size()>1)
    {
        t=dui.front();dui.pop();
        dui.push(merge(dui.front(),t));
        dui.pop();
    }
    if(dui.size())root=dui.front(),dui.pop();
    else root=0;
}
void in(){scanf("%d",&n);}
void ac()
{
    int op,x;
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&op);
        if(op==1)scanf("%d",&x),root=merge(_new(x),root);
        else if(op==2)printf("%d\n",val[root]);
        else pop();
    }
}
int main(){in();ac();}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值