左偏树简介(洛谷P3377题解)

来源:互联网 发布:欧楷笔法 知乎 编辑:程序博客网 时间:2024/06/08 10:17

(感觉自己算法学得好乱啊。。。)

算法简介

左偏树(Leftist Tree)是一种可并堆的实现。所谓可并堆,即既满足堆的性质,又支持堆的合并。
左偏树是一棵二叉树,它的节点除了和二叉树的节点一样具有左右子树指针外,还有两个属性,键值和距离。

算法实现

性质

每个节点都有一个“距离”,即该节点左右儿子距离的最小值+1(如果有一个儿子是没有的距离就是0)。而左偏树的每一个节点都满足这样一个性质:其左儿子的距离大于等于其右儿子的距离。

核心

左偏树算法的核心就是合并操作。对于两棵树x和y,有如下操作进行合并(实际合并时只需拿出根节点即可):
①如果其中一棵是空树,返回另一棵。
②如果x根节点的优先级比y的大,则交换x与y。
③递归合并y与x的右子树。
④如果右儿子的距离大于左儿子了,就把两个儿子交换(保证左偏性)
⑤更新x的距离。

模板

以洛谷P3377为例:

非路压版:

#include<cstdio>#include<cstring>#include<algorithm>#define MAXN 100000using namespace std;struct node{    int ls,rs;//左儿子与右儿子    int v,dis;//该节点的值与距离};int n,m,fa[MAXN+5];bool f[MAXN+5];node a[MAXN+5];//fread读优inline char readc(){    static char buf[100000],*l=buf,*r=buf;    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);    if (l==r) return EOF;    return *l++;}inline int _read(){    int num=0; char ch=readc();    while (ch<'0'||ch>'9') ch=readc();    while (ch>='0'&&ch<='9') { num=num*10+ch-48; ch=readc(); }    return num;}inline int findfather(int x){//这里不能路压,因为不是并查集    while (fa[x]) x=fa[x];    return x;}int merge(int x,int y){    if (x*y==0) return x+y;//如果其中一个为空返回另一个    if (a[x].v>a[y].v||(a[x].v==a[y].v&&x>y)) swap(x,y);//如果x的优先级比y的大,交换(这里是小根堆)    a[x].rs=merge(a[x].rs,y);//递归处理    fa[a[x].rs]=x;//更新右子树    if (a[a[x].ls].dis<a[a[x].rs].dis) swap(a[x].ls,a[x].rs);    //如果右儿子的距离大于左儿子,就把两个儿子交换    a[x].dis=a[a[x].rs].dis+1;//更新距离    return x;}int main(){    n=_read(); m=_read();    memset(f,false,sizeof(f));    for (int i=1;i<=n;i++){        a[i].v=_read();        a[i].dis=0;    }    for (int i=1;i<=m;i++){        int flag=_read();        if (flag==1){            int x=_read(),y=_read();            if (f[x]||f[y]) continue;            int fx=findfather(x),fy=findfather(y);            if (fx!=fy)                merge(fx,fy);        }        else{            int x=_read();            if (f[x]){                printf("-1\n");                continue;            }            int fx=findfather(x);            printf("%d\n",a[fx].v);            f[fx]=true;            fa[a[fx].ls]=fa[a[fx].rs]=0;            merge(a[fx].ls,a[fx].rs);        }    }    return 0;}

路压版(其实没什么变化):

#include<cstdio>#include<cstring>#include<algorithm>#define MAXN 100000using namespace std;struct node{    int ls,rs;    int v,dis;};int n,m,fa[MAXN+5];bool f[MAXN+5];node a[MAXN+5];inline char readc(){    static char buf[100000],*l=buf,*r=buf;    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);    if (l==r) return EOF;    return *l++;}inline int _read(){    int num=0; char ch=readc();    while (ch<'0'||ch>'9') ch=readc();    while (ch>='0'&&ch<='9') { num=num*10+ch-48; ch=readc(); }    return num;}int findfather(int x){//路压    if (fa[x]==x) return x;    return fa[x]=findfather(fa[x]);}int merge(int x,int y){//合并操作一个样    if (x*y==0) return x+y;    if (a[x].v>a[y].v||(a[x].v==a[y].v&&x>y)) swap(x,y);    a[x].rs=merge(a[x].rs,y);    fa[a[x].rs]=x;    if (a[a[x].ls].dis<a[a[x].rs].dis) swap(a[x].ls,a[x].rs);    if (a[x].rs) a[x].dis=a[a[x].rs].dis+1;    return x;}int main(){    n=_read(); m=_read();    memset(f,false,sizeof(f));    for (int i=1;i<=n;i++){        a[i].v=_read();        fa[i]=i;    }    for (int i=1;i<=m;i++){        int flag=_read();        if (flag==1){            int x=_read(),y=_read();            if (f[x]||f[y]) continue;            int fx=findfather(x),fy=findfather(y);            if (fx!=fy){                merge(fx,fy);            }        }        else{            int x=_read();            if (f[x]){                printf("-1\n");                continue;            }            int fx=findfather(x);            printf("%d\n",a[fx].v);            f[fx]=true;            fa[a[fx].ls]=a[fx].ls; fa[a[fx].rs]=a[fx].rs;            fa[fx]=merge(a[fx].ls,a[fx].rs);            a[fx].ls=a[fx].rs=0;        }    }    return 0;}
原创粉丝点击