• 洛谷


    https://www.luogu.org/problemnew/show/P3377
    左偏树+并查集

    左偏树维护两个可合并的堆,并查集维护两个堆元素合并后可以找到正确的树根。

    关键点在于删除一个堆的堆根的时候,需要把原来堆根的父指针指向新的堆根。这样并查集的性质就不会被破坏了。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    int solve();
    
    int main() {
    #ifdef Yinku
        freopen("Yinku.in","r",stdin);
    #endif // Yinku
        solve();
    }
    
    const int MAXN=100005;
    int tot,v[MAXN],l[MAXN],r[MAXN],d[MAXN],f[MAXN];
    //这其实是一片左偏树森林,用处不大
    struct Leftist_Tree{
        int Merge(int x,int y){
            //将两棵左偏树,返回他们的新根
            //当其中一棵是空树,直接返回另一棵
            if(!x||!y)
                return x+y;
            //此处符号决定是最小堆还是最大堆
            if(v[x]>v[y]||(v[x]==v[y]&&x>y))
                swap(x,y);
            //维持x不比y大,相等则把小的那个作为根
            //把大数y插入小数x的右子树,所以是最小堆
            r[x]=Merge(r[x],y);
            if(r[x]==x)
                r[x]=0;
            //保证左子树比右子树高
            //新的右子树的根的父亲是x,不需要可以删除
            f[r[x]]=x;
            if(d[l[x]]<d[r[x]])
                swap(l[x],r[x]);
            d[x]=d[r[x]]+1;
    
    
            return x;
        }
        int Build(int x){
            //新建一棵只有x的左偏树,返回他的编号
            tot++;
            v[tot]=x;
            //新树的节点的父亲为他本身,不需要可以删除
            f[tot]=tot;
            l[tot]=r[tot]=d[tot]=0;
            return tot;
        }
        int Push(int x,int y){
            //向编号为x的左偏树插入值为y的节点,其实就是新建只有y的左偏树然后合并
            return Merge(x,Build(y));
        }
        int Top(int x){
            //直接返回编号为x的左偏树的堆顶
            return v[Get_root(x)];
        }
        int Get_root(int x){
            //查找编号为x的节点所在的左偏树的根的序号,不需要可以删除
            int r=x;
            while(f[r]!=r)
                r=f[r];
            //路径压缩,直接指向他们的根
            int k;
            while(f[x]!=r){
                k=f[x];
                f[x]=r;
                x=k;
            }
            return r;
        }
        int Pop(int x){
            //将两个子树合并,相当于删除了堆顶
            //return Merge(l[x],r[x]);
            //当需要保留查询编号为x的节点在哪个子树时,不能够把堆顶丢掉
            //把编号为x的元素删除
            v[x]=-1;
            //根x被删除,从x反过来指向他们的孩子就可以了
            int rt=Merge(l[x],r[x]);
            f[x]=f[l[x]]=f[r[x]]=rt;
            return rt;
        }
    };
    
    int solve() {
        int n,m;
        scanf("%d%d",&n,&m);
        //printf("n,m=%d,%d
    ",n,m);
        Leftist_Tree lt;
        f[0]=0;
        v[0]=-1;
        tot=0;
        for(int i=1;i<=n;i++){
            int x;
            scanf("%d",&x);
            //printf("x=%d
    ",x);
            lt.Push(i,x);
            /*for(int j=1;j<=tot;j++){
                printf("%d ",v[j]);
            }
            printf("
    ");*/
        }
        for(int i=1;i<=m;i++){
            int z,x,y;
            scanf("%d",&z);
            //printf("z=%d
    ",z);
            if(z==1){
                scanf("%d%d",&x,&y);
                //printf("x=%d y=%d
    ",x,y);
                if(v[x]==-1||v[y]==-1)
                    continue;
                lt.Merge(lt.Get_root(x),lt.Get_root(y));
            }
            else{
                scanf("%d",&x);
                if(v[x]==-1){
                    printf("-1
    ");
                    continue;
                }
                //printf("x=%d
    ",x);
                int fx=lt.Get_root(x);
                printf("%d
    ",lt.Top(fx));
                lt.Pop(fx);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    JavaScript原型链详解
    Js作用域与闭包
    tjs 在嵌套函数中this关键字引用head对象
    NodeJS stream 一:Buffer
    NodeJS Stream 二:什么是 Stream
    NodeJS Stream 三:readable
    NodeJS Stream 四:Writable
    VSS又一次出错了,神出鬼没的
    VS2005的关于母版页嵌套的一个小技巧
    【转】SQL Server数据库开发的二十一条军规
  • 原文地址:https://www.cnblogs.com/Yinku/p/10957790.html
Copyright © 2020-2023  润新知