左偏树学习笔记
左偏树:能在 \(O(\log n)\) 复杂度内完成单次合并的堆
我们定义 \(dis[x]\) 表示 \(x\) 子树内 \(x\) 和最近空节点的距离 空节点 \(dis[0]=-1\)
我们让 \(dis[ls[x]]>dis[rs[x]]\) 这样左子树一定比右子树大 \(dis[x]=dis[rs[x]]+1\)
依照 Kazdale 所言:
可以把左子树当成重链 右子树当成轻链 这样把 \(y\) 和 \(rs[x]\) 合并复杂度一定就是 \(\log\) 级别的
那么merge操作:
inl int merge(int x,int y){
if(!x||!y)return x+y;
if(a[y]<a[x])swap(x,y);//让y为较大值
rs[x]=merge(rs[x],y);//y与rs[x]合并
if(dis[ls[x]]<dis[rs[x]])swap(ls[x],rs[x]);
//保证左偏树dis[ls[x]]>dis[rs[x]]
dis[x]=dis[rs[x]]+1;
return x;
}
操作需要找到当前点所在堆的根节点 那么可以记录每个点的fa暴力跳
实际上左子树深度可能是 \(O(n)\) 级别的 复杂度就回到 \(O(n^2)\) 了
那么可以用路径压缩并查集维护
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline
#define int ll
#define gc getchar
#define pc putchar
const int N=1e6+5;
const int M=1e8+5;
const int inf=0x3f3f3f3f;
const int mod=1e6+3;
inl int read(){
int x=0,f=1;char c=gc();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return x*f;
}
inl void write(int x){
if(x<0){pc('-');x=-x;}
if(x>9)write(x/10);
pc(x%10+'0');
}
inl void writei(int x){write(x);pc(' ');}
inl void writel(int x){write(x);pc('\n');}
int n,m,dis[N],rt[N],ls[N],rs[N],vis[N];
inl int find(int x){return rt[x]==x?x:rt[x]=find(rt[x]);}
struct node{
int v,id;
friend bool operator<(node a,node b){return a.v^b.v?a.v<b.v:a.id<b.id;}
}a[N];
inl int merge(int x,int y){
if(!x||!y)return x+y;
if(a[y]<a[x])swap(x,y);
rs[x]=merge(rs[x],y);
if(dis[ls[x]]<dis[rs[x]])swap(ls[x],rs[x]);
dis[x]=dis[rs[x]]+1;
return x;
}
signed main(){
n=read();m=read();dis[0]=-1;
for(int i=1;i<=n;i++)a[i]={read(),i},rt[i]=i;
while(m--){
int op=read();
if(op==1){
int x=read(),y=read();
if(vis[x]||vis[y])continue;
x=find(x),y=find(y);
if(x==y)continue;
rt[x]=rt[y]=merge(x,y);
}else{
int x=read();
if(vis[x]){writel(-1);continue;}
x=find(x);
writel(a[x].v);
vis[x]=1;
rt[ls[x]]=rt[rs[x]]=rt[x]=merge(ls[x],rs[x]);
}
}
return 0;
}
实际上还可以启发式合并:每次把大的合并到小的上
那么每次一个堆被合并 \(siz\) 至少 \(\times 2\)
每个元素最多被合并 \(\log n\) 次(再多就超出 \(n\) 了
每次合并插入复杂度 \(\log n\)
\(n\) 个元素 复杂度 \(O(n\log^2 n)\) 同样优秀
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline
#define int ll
#define gc getchar
#define pc putchar
const int N=1e5+5;
const int M=1e8+5;
const int inf=0x3f3f3f3f;
const int mod=1e6+3;
inl int read(){
int x=0,f=1;char c=gc();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return x*f;
}
inl void write(int x){
if(x<0){pc('-');x=-x;}
if(x>9)write(x/10);
pc(x%10+'0');
}
inl void writei(int x){write(x);pc(' ');}
inl void writel(int x){write(x);pc('\n');}
int n,m,dis[N],rt[N],vis[N],siz[N];
inl int find(int x){return rt[x]==x?x:rt[x]=find(rt[x]);}
struct node{
int v,id;
friend bool operator<(node a,node b){return a.v^b.v?a.v>b.v:a.id>b.id;}
};
priority_queue<node>q[N];
inl void merge(int x,int y){
if(siz[x]<siz[y])swap(x,y);
siz[x]+=siz[y];rt[y]=x;
while(!q[y].empty()){
node xx=q[y].top();q[y].pop();
q[x].push(xx);
}
}
signed main(){
n=read();m=read();
for(int i=1;i<=n;i++)q[i].push({read(),i}),rt[i]=i,siz[i]=1;
while(m--){
int op=read();
if(op==1){
int x=read(),y=read();
if(vis[x]||vis[y])continue;
x=find(x),y=find(y);
if(x==y)continue;
merge(x,y);
}else{
int x=read();
if(vis[x]){writel(-1);continue;}
x=find(x);
writel(q[x].top().v);
vis[q[x].top().id]=1;
q[x].pop();
}
}
return 0;
}

浙公网安备 33010602011771号