zoukankan      html  css  js  c++  java
  • 【模板】左偏树(可并堆)

    https://www.luogu.org/problemnew/show/3377

    主要是删除堆顶元素后并查集关系的维护:

    第一种方式(代码):

    原来的堆顶是x,删除x后,合并x的左右子树l、r,新的堆顶为y

    则令x的祖先指向y

    堆顶的直接子节点在并查集中的祖先指向堆顶

    这样在寻找l、r的祖先时,先找到x,再找到y

    x实际已经删除,但保留在并查集中,充当l、r找祖先节点的桥梁

    第二种方式:

    并查集不路径压缩

    删除x后,l的祖先指向l,r的祖先指向r

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    #define N 100001
    
    using namespace std;
    
    struct node
    {
        int lc,rc;
        int key,dis;
    }e[N];
    
    int fa[N];
    
    bool cut[N];
    
    void read(int &x)
    {
        x=0; int f=1; char c=getchar();
        while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
        x*=f;
    }
    
    int find(int i) { return fa[i]==i ? i : fa[i]=find(fa[i]); }
    
    int merge(int a,int b)
    {
        if(!a) return b;
        if(!b) return a;
        if(e[a].key>e[b].key) swap(a,b);
        e[a].rc=merge(e[a].rc,b);
        if(e[e[a].rc].dis>e[e[a].lc].dis) swap(e[a].lc,e[a].rc);
        if(!e[a].rc) e[a].dis=0;
        else e[a].dis=e[e[a].rc].dis+1;
        return a;
    }
    
    void erase(int x)
    {
        cut[x]=true;
        fa[x]=merge(e[x].lc,e[x].rc);
        fa[fa[x]]=fa[x];
    }
    
    int main()
    {
        int n,m;
        read(n); read(m);
        for(int i=1;i<=n;++i) 
        {
            read(e[i].key);
            fa[i]=i;
        }
        int ty,x,y;
        while(m--)
        {
            read(ty);
            if(ty==1)
            {
                read(x);
                read(y);
                if(cut[x]||cut[y]) continue;
                x=find(x);
                y=find(y);
                if(x!=y) fa[x]=fa[y]=merge(x,y);
            }
            else
            {
                read(x);
                if(cut[x]) { puts("-1"); continue; }
                x=find(x);
                cout<<e[x].key<<'
    ';
                erase(x);
            }
        }
    }

    题目描述

    如题,一开始有N个小根堆,每个堆包含且仅包含一个数。接下来需要支持两种操作:

    操作1: 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或第y个数已经被删除或第x和第y个数在用一个堆内,则无视此操作)

    操作2: 2 x 输出第x个数所在的堆最小数,并将其删除(若第x个数已经被删除,则输出-1并无视删除操作)

    输入输出格式

    输入格式:

    第一行包含两个正整数N、M,分别表示一开始小根堆的个数和接下来操作的个数。

    第二行包含N个正整数,其中第i个正整数表示第i个小根堆初始时包含且仅包含的数。

    接下来M行每行2个或3个正整数,表示一条操作,格式如下:

    操作1 : 1 x y

    操作2 : 2 x

    输出格式:

    输出包含若干行整数,分别依次对应每一个操作2所得的结果。

  • 相关阅读:
    HDU.6681.Rikka with Cake(欧拉公式 树状数组)
    Codeforces.449C.Willem, Chtholly and Seniorious(ODT)
    2017-2018 ACM-ICPC, Asia Daejeon Regional Contest (E,G,H,I,K)
    CF GYM.101987A.Circuits(线段树)
    2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)
    220
    219
    218
    217
    216
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8145758.html
Copyright © 2011-2022 走看看