【暖*墟】#数据结构# 左偏树的学习与练习

左偏树精髓部分:堆的合并

 

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\n",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\n"); 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\n",val[x_]); //两个堆合并
        }
    }
}

 

 

                                          ——时间划过风的轨迹,那个少年,还在等你

 

posted @ 2019-03-10 21:29  花神&缘浅flora  阅读(179)  评论(0编辑  收藏  举报