码迷,mamicode.com
首页 > 其他好文 > 详细

并不对劲的斜堆

时间:2018-01-29 19:20:16      阅读:216      评论:0      收藏:0      [点我收藏+]

标签:技术分享   main   重复   分享图片   splay   col   lmin   std   nod   

为了反驳隔壁很对劲的太刀流,并不对劲的片手流将与之针锋相对。

很对劲的斜堆、左偏树简明教程

它们是可并堆的两种实现方式。

(假装二叉堆只包括小根堆。)

二叉堆该如何合并?先想一种暴力的。

现在有根的键值较小的二叉堆A,键值较大的二叉堆B。

在合并后,A的根肯定还是根。若A的左、右子树都不为空的话,则可以随便选一个,再将这个堆与B合并。

递归地重复这个过程,直到左、右子树中有一方为空,直接接过去就行了。

然而这样时间复杂度并不会得到保障,因为每次“随便选”的那一个可能更深。这样时间复杂度就玄学了。

斜堆则是在暴力的基础上有一些修改。每次必选右(或左)子树合并,然后交换左右子树。这样下次就轮到这次没能合并的子树与之合并了。

这听上去和暴力区别不大,还有那么一些些的扯。但是类似于splay,它的时间复杂度均摊却是O(n)的。至于证明,类似于splay,并不对劲的人并不知道,就感性理解吧。

加点操作相当于与一个只有一个节点的斜堆合并。

删除相当于去掉堆顶元素,再将它的左、右子树合并。

可以去洛谷p3377测评,但是斜堆不是MLE就是TLE。

技术分享图片
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define maxn 100010
using namespace std;
int read()
{
    int f=1,x=0;char ch=getchar();
    while(isdigit(ch)==0 && ch!=-)ch=getchar();
    if(ch==-)f=-1,ch=getchar();
    while(isdigit(ch))x=x*10+ch-0,ch=getchar();
    return x*f;
}
void write(int x)
{
    int ff=0;char ch[15];
    if(x<0)
    {
        x=-x;
        putchar(-);
    }
    while(x)ch[++ff]=(x%10)+0,x/=10;
    if(ff==0)putchar(0);
    while(ff)putchar(ch[ff--]);
    putchar(\n);
}
typedef struct node
{
    int key,ls,rs,siz;
}heapnode;
struct InterestingHeap
{
    heapnode xx[maxn];
    int rt[maxn],fa[maxn],n,q;
    int x,y,root,son,l,r,tx,ty; 
    bool vis[maxn];
/*    void printrt()
    {
        cout<<"rt:"<<endl;
        for(int i=1;i<=n;i++)
            cout<<rt[i]<<" ";
        cout<<endl;
    }
    void printfa()
    {
        cout<<"fa:"<<endl;
        for(int i=1;i<=n;i++)
            cout<<fa[i]<<" ";
        cout<<endl;
    }*/ 
    bool cmp(int _1,int _2)//Smaller.
    {
        return xx[_1].key==xx[_2].key?_1<_2:xx[_1].key<xx[_2].key;
    }
    int f(int x){return fa[x]<0?x:fa[x]=f(fa[x]);} 
    void add(int x,int y)//The father is Tx.
    {
        tx=f(x),ty=f(y);
        if(tx==ty)return;
        fa[ty]=tx;
    }
    int merge(int A,int B) 
    {
        if(!xx[A].siz)return B;
        if(!xx[B].siz)return A;
        if(!cmp(A,B))swap(A,B);
        xx[A].rs=merge(xx[A].rs,B);
        swap(xx[A].ls,xx[A].rs);
        xx[A].siz=xx[xx[A].ls].siz+xx[xx[A].rs].siz;
        return A;
    }
    void getit()
    {
        x=read(),y=read();
        if(vis[x] || vis[y])return;
        tx=f(x),ty=f(y);
        if(tx==ty)return;
        if(!cmp(tx,ty))swap(tx,ty);
        merge(tx,ty);
        add(tx,ty);
    }
    void delmin(int u)
    {
        l=xx[u].ls,r=xx[u].rs;
        //cout<<u<<" "<<l<<"-"<<r<<endl;
        if(l==0 && r==0)return;
        if(l==0 || r==0)
        {
            son=l==0?r:l;
            fa[son]=fa[u],fa[u]=son;
            rt[-fa[u]]=son;
            return;
        }
        if(!cmp(l,r))swap(l,r);
        fa[l]=fa[u],fa[r]=l;fa[u]=l;
        rt[-fa[u]]=l;
        merge(l,r);
    }
    void ask()
    {
    //    printfa();
        x=read();
        if(vis[x]){write(-1);return;}
        root=rt[-fa[f(x)]];
        vis[root]=1;
        write(xx[root].key);
        delmin(root);
    //    printfa();
    }
    void work()
    {
        memset(vis,0,sizeof(vis));
        n=read(),q=read();
        for(int i=1;i<=n;i++)
        {
            xx[i].key=read();
            xx[i].siz=1;
            rt[i]=i;
            fa[i]=-i;
        }
        int f;
        while(q--)
        {
            f=read();
            if(f==1)
                getit();
            else 
                ask();
        }
    }
}t;
int main()
{
    t.work();
    return 0;
}
/*
5 5
1 5 4 2 3
1 1 5
1 2 5
2 2
1 4 2
2 2
*/
并不对劲的斜堆

 

并不对劲的斜堆

标签:技术分享   main   重复   分享图片   splay   col   lmin   std   nod   

原文地址:https://www.cnblogs.com/xzyf/p/8378223.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!