左偏树精髓部分:堆的合并
int merge(int x,int y){ //*左偏树精髓*(大顶堆,返回堆顶元素) if(x==0||y==0) return (x+y); if(val[x]<val[y]) swap(x,y); //大顶堆 ch[x][1]=merge(ch[x][1],y); fa[ch[x][1]]=x; //将一个堆的右子树和另一个堆比较 if(dis[ch[x][0]]<dis[ch[x][1]]) swap(ch[x][0],ch[x][1]); //维护左偏性质 if(ch[x][1]==0) dis[x]=0; else dis[x]=dis[ch[x][1]]+1; return x; }
左偏树模板题:洛谷 p3377
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<string> #include<queue> #include<vector> #include<cmath> #include<map> using namespace std; typedef long long ll; /*【p3377】左偏树 —— 可并堆 一开始有N个小根堆,每个堆包含且仅包含一个数。 操作1: 1 x y 将第x个数和第y个数所在的小根堆合并, 操作2: 2 x 输出第x个数所在的堆最小数,并将其删除。*/ //需要支持两个堆的合并,不能使用priority queue,需要手写左偏树。 //感觉背代码比看文字更容易理解2333 void reads(int &x){ //读入优化(正负整数) int fx=1;x=0;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')fx=-1;s=getchar();} while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();} x*=fx; //正负号 } const int N=100019; int ch[N][2],val[N],dis[N],fa[N],n,m; void swap(int &x,int &y){int t=x;x=y,y=t;} int find_fa(int x){ return fa[x]=(fa[x]==x)?x:find_fa(fa[x]); } int merge(int x,int y){ //*左偏树精髓* if(x==0||y==0) return (x+y); if(val[x]>val[y]||(val[x]==val[y]&&x>y)) swap(x,y); ch[x][1]=merge(ch[x][1],y); //↓↓维护左偏性质 if(dis[ch[x][0]]<dis[ch[x][1]]) swap(ch[x][0],ch[x][1]); dis[x]=dis[ch[x][1]]+1,fa[ch[x][1]]=fa[ch[x][0]]=x; return x; } void pop(int x){ val[x]=-1,fa[ch[x][0]]=ch[x][0], //↓↓注意这里一定要更新fa[x] fa[ch[x][1]]=ch[x][1],fa[x]=merge(ch[x][0],ch[x][1]); } int main(){ reads(n),reads(m); dis[0]=-1; for(int i=1;i<=n;i++) reads(val[i]),fa[i]=i; for(int i=1,op,x,y;i<=m;i++){ reads(op); if(op==1){ reads(x),reads(y); if(val[x]==-1||val[y]==-1) continue; //已经删除 int fx=find_fa(x),fy=find_fa(y); merge(fx,fy); } if(op==2){ reads(x); if(val[x]==-1){ puts("-1"); continue; } int y=find_fa(x); printf("%d ",val[y]); pop(y); } } }
左偏树练习题:洛谷 p1456
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<string> #include<queue> #include<vector> #include<cmath> #include<map> using namespace std; typedef long long ll; //【p1456】MK 必胜!(疯狂给小猴子打电话233) void reads(int &x){ //读入优化(正负整数) int fx=1;x=0;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')fx=-1;s=getchar();} while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();} x*=fx; //正负号 } const int N=100019; int ch[N][2],val[N],dis[N],fa[N],n,m; int find_fa(int x){ return fa[x]=(fa[x]==x)?x:find_fa(fa[x]); } int merge(int x,int y){ //*左偏树精髓*(大顶堆,返回堆顶元素) if(x==0||y==0) return (x+y); if(val[x]<val[y]) swap(x,y); //大顶堆 ch[x][1]=merge(ch[x][1],y); fa[ch[x][1]]=x; //将一个堆的右子树和另一个堆比较 if(dis[ch[x][0]]<dis[ch[x][1]]) swap(ch[x][0],ch[x][1]); //维护左偏性质 if(ch[x][1]==0) dis[x]=0; else dis[x]=dis[ch[x][1]]+1; return x; } int pop(int x) { int l=ch[x][0],r=ch[x][1]; fa[l]=l,fa[r]=r, ch[x][0]=ch[x][1]=dis[x]=0; return merge(l,r); } int main(){ while(scanf("%d",&n)!=EOF){ for(int i=1;i<=n;i++) fa[i]=i,ch[i][0]=ch[i][1]=0,dis[i]=0; for(int i=1;i<=n;i++) reads(val[i]); reads(m); for(int i=1,x,y;i<=m;i++){ reads(x),reads(y); int fx=find_fa(x),fy=find_fa(y); if(fx==fy){ printf("-1 "); continue; } val[fx]/=2,val[fy]/=2; //打斗以后权值减半 //for(int i=1;i<=n;i++) cout<<val[i]<<" -- "; cout<<endl; int x_=pop(fx),y_=pop(fy); //删除根节点之后合并 x_=merge(x_,fx),y_=merge(y_,fy); //堆和点合并 x_=merge(x_,y_); printf("%d ",val[x_]); //两个堆合并 } } }
——时间划过风的轨迹,那个少年,还在等你