其他分享
首页 > 其他分享> > 洛谷 P3377 【模板】左偏树(可并堆)

洛谷 P3377 【模板】左偏树(可并堆)

作者:互联网

传送门


左偏树

政治老师告诉我们,要从以下方面思考问题:

  1. 是什么
  2. 为什么
  3. 怎么做

1.是什么:嗯……从字面看,是树(堆),是一个长得向左偏的二叉树(堆)。就长这样:

定义们:

外节点:当且仅当节点 i 的左子树或右子树为空时,节点被称作外节点。

距离:一个点的距离,被定义为它子树中离他最近的外节点到这个节点的距离。

性质们:

  1. 满足二叉堆的性质
  2. 左儿子的距离大于等于右儿子的距离(所以左偏)
  3. 节点的距离等于它的右子节点的距离加1(显然)
  4. N个节点的左偏树root节点的距离最多为log(N+1)-1(保证了效率)(证明?懒得写了嘿嘿嘿)

2.为什么(或者说能干啥):能实现两个堆之间的合并O(logN+logM) 。普通的堆需要把其中一个堆里的元素一个一个插入另一个堆里,是O(NlogM)的。还能实现删除、插入等基本操作。

3.怎么做:主要是合并merge操作。

假设合并两个小根堆A和B,比较堆顶大小决定堆顶是谁(假设B)。然后A堆跟B堆的右儿子合并,递归上述操作。期间需要确定一个点的祖宗,用到并查集的find操作,要路径压缩。

对于删除操作,就是相当于把堆顶的两个儿子的堆合并起来。

对于单点插入操作,就是把这一个点看做一个堆,与要插入的堆合并。

注意在删除操作时,因为进行了路径压缩,所以某些节点的fa是这个删除的点(不一定是儿子)。这是就应该把儿子的fa改成本身,把删除的这个节点的fa改成新堆顶。

AC代码

 1 #include<iostream>
 2 #include<cstring> 
 3 #include<cstdio>
 4 using namespace std;
 5 const int maxn=100005;
 6 int n,m,a[maxn],fa[maxn],vis[maxn];
 7 struct node{
 8     int rs,ls,dis;
 9 }t[maxn];
10 int find(int x){
11     if(fa[x]==x) return x;
12     return fa[x]=find(fa[x]);
13 }
14 void pushup(int x){
15     if(t[t[x].rs].dis>t[t[x].ls].dis) swap(t[x].rs,t[x].ls);
16     t[x].dis=t[t[x].rs].dis+1;
17 }
18 int merge(int x,int y){
19     if(!x||!y) return x|y;
20     if(vis[x]||vis[y]) return vis[x]?x:y;
21     if(a[x]>a[y]||(a[x]==a[y]&&x>y)) swap(x,y);
22     t[x].rs=merge(t[x].rs,y);
23     fa[t[x].rs]=x;
24     pushup(x);
25     return x;
26 }
27 void goout(int x){
28     fa[t[x].ls]=t[x].ls;
29     fa[t[x].rs]=t[x].rs;
30     int newtp=merge(t[x].ls,t[x].rs);
31     fa[x]=newtp;
32 }
33 int main(){
34     cin>>n>>m;
35     for(int i=1;i<=n;i++) fa[i]=i;
36     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
37     for(int i=1;i<=m;i++){
38         int id,x,y;
39         scanf("%d",&id);
40         if(id==1){
41             scanf("%d%d",&x,&y);
42             if(vis[x]||vis[y]) continue;
43             x=find(x);
44             y=find(y);
45             if(x==y) continue;
46             merge(x,y);
47         }else{
48             scanf("%d",&x);
49             if(vis[x]) printf("-1\n");
50             else{
51                 int tp=find(x);
52                 printf("%d\n",a[tp]);
53                 goout(tp);
54                 vis[tp]=1;
55             }
56         }
57     }
58     return 0;
59 }

 

标签:洛谷,rs,int,fa,ls,P3377,节点,左偏
来源: https://www.cnblogs.com/yinyuqin/p/14058893.html