左偏树学习笔记

左偏树学习笔记

先给出定义吧

大概就是在堆的基础上要求维护一个另外的性质

定义 dis[i]为i到自己子树中所有不满足 有两个儿子 这个条件的节点中 最短 距离

 

所以它大概长这个样子

 

 

 

 

要求维护堆的结构 使得对于一个节点 U 满足 dis[ls[U]] ≥ dis[rs[U]

可以看出这其实是一个极其不平衡的一个堆(树)

所以我们要它干什么呢?赶紧写splay转啊

其实主要是用于合并两棵树用的

先考虑它有啥性质吧

根的深度不超过 log(n)

节点的权值小于等于左右儿子的权值

dis[u] = dis[rs[u]]+1; (因为一定是左右儿子中最小的+1

左偏树的距离为一个定值,则为完全二叉树

还挺显然的 

使用以上这些性质就可以做到:

  合并!!!

 是的,不需要动态开点splay,您可以在 O(logn) 复杂度以内实现两个左偏树的合并

具体实现如下:

1. 如果x的根节点val大于y根节点的val,那么swap(x,y),目的是保证把大的合并到小的上。
2. 将y的根和x的右子树判断是否满足1的规则,递归合并,缩小规模,直到一个叶子节点,那么直接修改 tree[x].rc=y,这样完成合并。
3. 如果在合并过程中出现了 距离不满足左大于右 的情况了 那么就直接 
4. 合并完之后,dis[u]=dis[rs]+1

 

具体实现:

 

struct mergeheap
{
    int val[maxn],ff[maxn],ls[maxn],rs[maxn],dis[maxn];
    bool vis[maxn];
    void init(int n) {inc(i,1,n) rt[i]=i;dis[0]=-1;}
    int find(int x) {return (ff[x]==x) ? x : ff[x]=find(ff[x]);}
    int link(int x,int y) 
    {
        if(!x||!y) return x|y;
        if((val[x]>val[y]) || (val[x]==val[y]&&x>y)) swap(x,y);
        rs[x]=link(rs[x],y);
        if(dis[ls[x]]<dis[rs[x]]) swap(ls[x],rs[x]);
        dis[x]=dis[rs[x]]+1;
        return ff[ls[x]]=ff[rs[x]]=x;
    }
    void merge(int x,int y)
    {
        if(vis[x] || vis[y]) return;
        int x=find(x) , y=find(y);
        if(x!=y) ff[x]=ff[y]=link(x,y);
    }
    int top(int x)
    {
        if(vis[x]) return -1;
        x=find(x);
        retutrn val[x];
    }
    void pop(int x)
    {
        if(vis[x]) return;
        x=find(x);
        vis[x]=1;
        ff[x]=ff[ls[x]]=ff[rs[x]]=link(ls[x],rs[x]);
    }
}h;

 

 

就先这样吧(x

 

posted @ 2019-10-16 11:08  ZzTzZ  阅读(166)  评论(0)    收藏  举报