左偏树简介(洛谷P3377题解)
2017-09-25 21:25
295 查看
(感觉自己算法学得好乱啊。。。)
左偏树是一棵二叉树,它的节点除了和二叉树的节点一样具有左右子树指针外,还有两个属性,键值和距离。
①如果其中一棵是空树,返回另一棵。
②如果x根节点的优先级比y的大,则交换x与y。
③递归合并y与x的右子树。
④如果右儿子的距离大于左儿子了,就把两个儿子交换(保证左偏性)
⑤更新x的距离。
非路压版:
路压版(其实没什么变化):
算法简介
左偏树(Leftist Tree)是一种可并堆的实现。所谓可并堆,即既满足堆的性质,又支持堆的合并。左偏树是一棵二叉树,它的节点除了和二叉树的节点一样具有左右子树指针外,还有两个属性,键值和距离。
算法实现
性质
每个节点都有一个“距离”,即该节点左右儿子距离的最小值+1(如果有一个儿子是没有的距离就是0)。而左偏树的每一个节点都满足这样一个性质:其左儿子的距离大于等于其右儿子的距离。核心
左偏树算法的核心就是合并操作。对于两棵树x和y,有如下操作进行合并(实际合并时只需拿出根节点即可):①如果其中一棵是空树,返回另一棵。
②如果x根节点的优先级比y的大,则交换x与y。
③递归合并y与x的右子树。
④如果右儿子的距离大于左儿子了,就把两个儿子交换(保证左偏性)
⑤更新x的距离。
模板
以洛谷P3377为例:非路压版:
#include<cstdio> #include<cstring> #include<algorithm> #define MAXN 100000 using 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 100000 using 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; }
相关文章推荐
- Trie树简介 ( 洛谷P2580题解 )
- 洛谷P3377 【模板】左偏树(可并堆)
- 【BZOJ】【P2809】【APIO2012】【dispatching】【题解】【左偏树】
- 【洛谷P3377】【模板】左偏树(可并堆)
- 【可并堆】【数据结构】左偏树简介
- 模板 可并堆【洛谷P3377】 【模板】左偏树(可并堆)
- 左偏树/斜堆/可并堆-洛谷P3377 【模板】左偏树(可并堆)
- 【HDU1512】【左偏树】Monkey King 题解
- IOS Quartz2D简介
- Linux下的lds链接脚本简介(二)
- LaTeX入门学习(1)(简介)
- 推荐算法简介
- ASIHTTPRequest类库简介和使用说明
- 3D中的切线空间简介(转)
- [转]matlab的Virtual Reality(虚拟现实)工具箱的应用简介
- Java 线程池ThreadPoolExecutor使用简介
- Hadoop系列一HDFS简介
- RPKM简介
- Flex 选项卡加载方式简介
- WGSI 简介