luogu_P3377 左偏树(可并堆)
Posted coclhy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu_P3377 左偏树(可并堆)相关的知识,希望对你有一定的参考价值。
传送门:https://www.luogu.org/problem/P3377
左偏树:左偏!也就是下面这种左边大,右边小的树
可并堆:可以合并的堆(堆:维护最值的数据结构)
核心(细节):
先来代码
int merge(int x,int y){ if(!x||!y) return x+y; if(val[x]>val[y] || (val[x]==val[y]&&x>y)) swap(x,y); rs[x]=merge(rs[x],y); rt[rs[x]]=rt[ls[x]]=x; if(h[rs[x]]>h[ls[x]]) swap(rs[x],ls[x]); h[x]=h[rs[x]]+1; return x; }
在合并时,往右子树走,当发现右边的值不合法时,则另另一个堆的值来swap
以最大值为例: 当发现x<y时,让堆的右儿子为y,把原来那颗子树扯出来,继续合并
每次合并完之后更新高度(一个节点的高度=它的右儿子高度+1)
还有一点就是在删除的时候,先找到堆顶,删除,然后merge(rs[x],ls[x])
记得令 root[x]=merge(rs[x],ls[x]) 因为有路径压缩,所以有很多节点的top还是x
#include<cstdio> #include<algorithm> #define R register using namespace std; int n,m,val[100100],rt[100100],ls[100100],rs[100100],h[100100]; int find(int x){ return rt[x]==x? x:rt[x]=find(rt[x]); } int merge(int x,int y){ if(!x||!y) return x+y; if(val[x]>val[y] || (val[x]==val[y]&&x>y)) swap(x,y); rs[x]=merge(rs[x],y); rt[rs[x]]=rt[ls[x]]=x; if(h[rs[x]]>h[ls[x]]) swap(rs[x],ls[x]); h[x]=h[rs[x]]+1; return x; } void pop(int x){ val[x]=-1;rt[rs[x]]=rs[x];rt[ls[x]]=ls[x]; rt[x]=rt[rs[x]]=rt[ls[x]]=merge(rs[x],ls[x]); return; } int main (){ scanf("%d%d",&n,&m); for(R int i=1;i<=n;i++){ scanf("%d",&val[i]);rt[i]=i; } h[0]=-1; for(R int c,x,y,i=1;i<=m;i++){ scanf("%d",&c); if(c==1){ scanf("%d%d",&x,&y); if(val[x]==-1|| val[y]==-1) continue; x=find(x);y=find(y); if(x==y) continue; rt[x]=rt[y]=merge(x,y); } else{ scanf("%d",&x); if(val[x]==-1) printf("-1 "); else { y=find(x); printf("%d ",val[y]); pop(y); } } } return 0; }
以上是关于luogu_P3377 左偏树(可并堆)的主要内容,如果未能解决你的问题,请参考以下文章