• 浅谈左偏树


    可并堆

    可并堆顾名思义就是可以合并的堆。

    这里不讲二项堆和斐波那契堆,只讲左偏树。

    左偏树

    左偏树顾名思义就是向左偏的树。

    给每个点定义一个(dist),满足下面三个条件:

    1、空结点的(dist)等于(-1)

    2、每个结点的左儿子的(dist)都大于右儿子的(dist)

    3、每个结点的(dist)都等于右儿子的(dist+1)

    根据上面这些性质,我们可以推出左偏树中根结点的(dist)最大不超过(logsize)

    合并

    左偏树合并非常简单,假设我要合并(a,b)两颗树并且(val_a<val_b)(为了满足小根堆性质,不满足就交换(a,b)

    如果(a)(b)为空返回另一个结点

    否则合并(a)的右儿子和(b),如果这个时候右儿子的(dist)大于左儿子的(dist)就交换(a)的左右儿子,更新(a)(dist)然后返回(a)

    由于每一层递归的(dist_a+dist_b)都会比上一层小(1),最小可以到(-1),所以时间复杂度是(O(logsize_a+logsize_b))的。

    模板题传送门:https://www.luogu.org/problemnew/show/P3377

    时间复杂度:(O(mlogn))

    空间复杂度:(O(n))

    代码如下:

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int maxn=1e5+5;
    
    int n,m;
    int son[maxn][2];
    int v[maxn],fa[maxn],dist[maxn];
    
    int read() {
        int x=0,f=1;char ch=getchar();
        for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
        for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
        return x*f;
    }
    
    int find(int x) {
        while(fa[x])x=fa[x];
        return x;//由于树型结构会改变所以不敢路径压缩
    }
    
    int merge(int a,int b) {
        if(!a||!b)return a+b;
        if(v[a]>v[b])swap(a,b);
        son[a][1]=merge(son[a][1],b);
        fa[son[a][1]]=a;
        if(dist[son[a][1]]>dist[son[a][0]])
            swap(son[a][1],son[a][0]);
        dist[a]=dist[son[a][1]]+1;
        return a;
    }
    
    void pop(int u) {
        printf("%d
    ",v[u]);v[u]=-1;
        fa[son[u][0]]=fa[son[u][1]]=0;
        merge(son[u][0],son[u][1]);
        son[u][0]=son[u][1]=0;
    }
    
    int main() {
        n=read(),m=read();
        for(int i=1;i<=n;i++)
            v[i]=read();
        for(int i=1;i<=m;i++) {
            int opt=read();
            if(opt==1) {
                int x=read(),y=read();
                if(v[x]==-1||v[y]==-1)continue;
                x=find(x),y=find(y);
                if(x==y)continue;
                merge(x,y);
            }
            else {
                int u=read();
                if(v[u]==-1) {puts("-1");continue;}
                u=find(u),pop(u);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    git操作
    计算天数
    web小结~2019.3.24
    数据统计值的计算+PYTHON
    python~序列类型及操作
    一个日期加上若干天后是什么日期
    完数与盈数
    分段函数
    求最大最小
    D进制的A+B
  • 原文地址:https://www.cnblogs.com/AKMer/p/10246635.html
Copyright © 2020-2023  润新知