左偏树学习记录
可并堆之左偏树
(适用统计问题、最值问题、模拟问题和贪心问题)
一句话:符合堆、二叉树性质,且可实现快速合并
外节点:左儿子或右儿子为空的节点
(默认小根堆)
dist:
一个外结点的dist为1
空节点的dist为0
非外结点的dist为到达其子树的[最近的外结点]的距离+1
左偏树[左偏]:
二叉树的每个节点都满足[左儿子dist>=右儿子dist]
所以每个节点的dist一定等于其 [右儿子dist+1]
merge操作:
合并时,选取值较小的根节点cur作为合并后的根节点
cur的左儿子作为合并后堆的左儿子
cur的右儿子与另一个堆合并,作为新的右儿子
合并后如果[左儿子dist]<[右儿子dist],交换两个儿子
My_code:
//左偏树模板 luogu P3377 【模板】左偏树(可并堆)
#include <bits/stdc++.h>
using namespace std;
const int N=2e6+10;
int read() {
int x=0,f=1;
char ch=getchar();
while(ch<48||ch>57) {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>=48&&ch<=57) {
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return x*f;
}
struct leftTree{
int l,r;
int val,dist;
int rt;//father 用于记录所属堆顶,并查集查找
}t[N];
int find(int x){//并查集确定堆顶(堆顶代表这个堆)
if(t[x].rt==x) return x;
return t[x].rt=find(t[x].rt);
}
int merge(int x,int y){//合并,并返回合并后的根节点编号
if(!x||!y) return x|y;
if(t[x].val>t[y].val) swap(x,y);
if(t[x].val==t[y].val&&x>y) swap(x,y);// 题目要求:若有多个最小数,优先删除先输入的
t[x].r=merge(t[x].r,y);//合并右儿子与另一个堆
if(t[t[x].r].dist > t[t[x].l].dist) swap(t[x].l,t[x].r);//维护左儿子dist>右儿子dist性质
t[t[x].l].rt=t[t[x].r].rt=t[x].rt=x;//update father(rt)
t[x].dist=t[t[x].r].dist+1;//每个节点的dist一定等于其 [右儿子dist+1]
return x;
}
void del(int x){//删除
t[x].val=-1;
t[t[x].l].rt=t[x].l;//拆出左右儿子
t[t[x].r].rt=t[x].r;
t[x].rt=merge(t[x].l,t[x].r);//合并成新堆
}
int n,m;
int main(){
n=read();
m=read();
t[0].dist=-1;
for(int i=1;i<=n;i++){
t[i].val=read();
t[i].rt=i;//初始father为自己
}
while(m--){
int op=read(),x=read();
if(op==1){
int y=read();
if(t[x].val==-1||t[y].val==-1) continue;//已删除
x=find(x),y=find(y);
if(x!=y) t[x].rt=t[y].rt=merge(x,y);
}
else {
if(t[x].val==-1) printf("-1\n");
else {
x=find(x);
printf("%d\n",t[x].val);
del(x);
}
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?