左偏树简介(洛谷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;}
阅读全文
1 0
- 左偏树简介(洛谷P3377题解)
- 洛谷P3377 左偏树
- 【洛谷P3377】【模板】左偏树(可并堆)
- 左偏树/斜堆/可并堆-洛谷P3377 【模板】左偏树(可并堆)
- 【模板】左偏树 (模板题:洛谷P3377左偏树/可并堆)
- Trie树简介 ( 洛谷P2580题解 )
- 洛谷P2007 魔方 题解(矩阵乘法)
- 洛谷P1048采药题解
- 洛谷P1346 电车题解
- 洛谷 比赛题解
- [洛谷P3395]路障题解
- 洛谷P2393题解
- 洛谷P1816 忠诚 题解
- 洛谷P3931“tree”题解
- TYVJ题解(P1025)
- 数列(sequence) 题解
- uva11552题解(dp)
- 路径(path)题解
- socket 编程 服务器端-客户端
- 对于数据去重的处理-PDI导入前及数据库端的双重设定
- c/c++面试题2
- validate-binary-search-tree
- [iPhone]解决:手指按住不动时,屏幕也会上下抖动
- 左偏树简介(洛谷P3377题解)
- Solr区间查询背后原理
- 分治:循环赛日程表(递归+非递归)
- android7.0 电源(Power)键流程
- java 设计模式
- 齐一都在唱什么呢+歌词词云分析
- [DP]Codeforces 743E Vladik and cards
- UI游戏框架(一)
- luogu3383 线性筛模板