[数据结构] 三十二叉堆

模板链接: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

题解

张瀚文老师真是太毒瘤有才啦!!!orz

三十二叉堆,顾名思义,就是有三十二叉的堆,我们可以将每个数看做一个长为 32 32 01 01 串,把 5 5 01放在一起看做一个字母,构成一个类似 Trie T r i e 树的结构,就可以把一个 int i n t 范围的数插入到三十二叉堆中。

问题在于,我们怎么快速找到最小值呢???总不能遍历这三十二叉吧。

看到这个三十二,有没有想起什么?没错,一个 int i n t 就是三十二位的,我们可以把一个节点三十二叉的存在情况压到一个 int i n t 里!!!通过调用__builtin_ctz函数,我们就能快速知道最小的且存在的那一叉在哪儿,便可以直接跳到下一层节点了。

这样我们每次操作就是 O(log2MAXlog232) O ( l o g 2 M A X l o g 2 32 ) 的了,常数也很小,而且可以同时维护最大最小值,甚至可以变成一个 set s e t !!!

代码
#include<bits/stdc++.h>
#define uint unsigned int
using namespace std;
const int N=1e6,M=N<<2;
uint bit[M],inf=INT_MAX;
int son[M][32],cot[M],di[10],val[M],tot,n;
int lg(uint x){return __builtin_ctz(x);}
void ins(int x)
{
    int v=0,t=x;
    for(int i=7;i>=1;--i,x>>=5)di[i]=x%32;
    for(int i=1;i<=7;++i){++cot[v];if(!son[v][di[i]])son[v][di[i]]=++tot;bit[v]|=(1u<<di[i]);v=son[v][di[i]];}
    ++cot[v];val[v]=t;
}
int top()
{
    if(!cot[0])return 0;int v=0;
    for(int i=1;i<=7;++i)v=son[v][lg(bit[v])];
    return val[v];
}
void pop()
{
    if(!cot[0])return;
    int v=0,f;uint tmp;
    for(int i=1;i<=7;++i){tmp=lg(bit[v]);f=v;v=son[v][tmp];if(cot[v]==1)bit[f]&=inf<<tmp+1;--cot[f];}
    cot[v]--;
}
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),ins(x);
        else if(op==2)printf("%d\n",top());
        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、付费专栏及课程。

余额充值