LUOGU 3377 左偏树

Description

如题,一开始有N个小根堆,每个堆包含且仅包含一个数。接下来需要支持两种操作:
操作1 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或第y个数已经被删除或第x和第y个数在用一个堆内,则无视此操作)
操作2 2 x 输出第x个数所在的堆最小数,并将其删除(若第x个数已经被删除,则输出-1并无视删除操作)

Input Format

第一行包含两个正整数N,M,分别表示一开始小根堆的个数和接下来操作的个数。
第二行包含N个正整数,其中第i个正整数表示第i个小根堆初始时包含且仅包含的数。
接下来M行每行2个或3个正整数,表示一条操作,格式如下:
操作1 1 x y
操作2 2 x

Output Format

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

Sample Input

5 5
1 5 4 2 3
1 1 5
1 2 5
2 2
1 4 2
2 2

Sample Output

1
2

Constraints

对于100%的数据:N \leq 100000,M \leq 100000

CCYOS

这是一道左偏树板子题。感谢徐灏诚juju的讲课,挺好理解的。

  1. 左偏树
    可并堆的一种,有如下性质:
    a.堆性质
    保证每颗子树的根节点的权值小于它的左儿子和右儿子的权值,这样可以O(1)删除本堆中的最小节点。
    b.左偏性质
    定义有一颗子树为空的节点为外节点
    定义每个点的dist为这个点到它的后代中的外节点的最短距离
    左偏树满足dist[rs] \leq dist[ls]但是这并不代表左偏树左子树比右子树深度大
    所以有dist[x] = dist[ls] + 1不可能会出现单右子树的情况,所以令dist[0] = -1来使下图的根(外节点)的dist正确。
    权值为5的节点的dist为0

2)左偏树的合并
设有两棵左偏树A,B需要合并。
左偏树的合并是努力让B和A的右子树合并,简称把我的兄弟(?)变成我的儿子,同时要维护左偏树的性质。
a.有一棵树为空:直接返回该子树。

if(!x||!y)return x + y;

b.没有空子树:
为了维护堆性质,选取A,B中根节点权值较小的左偏树作为A树。
向下合并,将A的右儿子和B合并的树的根节点作为A的右儿子。
为了维护左偏性质,若左儿子的dist值小于右儿子的dist值,交换左右儿子。
更新A树根节点的dist,rt及左右儿子的rt
返回合并完毕的左偏树A

inline int Merge(int x,int y){
    if(!x || !y)return x + y;
    if(tr[x].val > tr[y].val)swap(x,y);
    if(tr[x].val == tr[y].val && x > y)swap(x,y);
    rs = Merge(rs,y);
    if(tr[ls].dis < tr[rs].dis)swap(ls,rs);
    tr[ls].rt = tr[rs].rt = tr[x].rt = x;
    tr[x].dis = tr[rs].dis + 1;
    return x;
}
  1. 左偏树的删除
    仅限于删除最小节点。
    O(1)删除最小节点,重置左右儿子的根,合并左右儿子。
inline void Pop(int x){
    tr[x].val = -1;
    tr[ls].rt = ls;tr[rs].rt = rs;
    tr[x].rt = Merge(ls,rs);
}

注意
a) Merge(rs,y)要更新给rs。
b) 注意题目要求的输出。
c) tr[0].dist = -1。
code

// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;

int N,M;

struct tree{
    int Ls,Rs,rt,val,dis;
}tr[100005];

inline int read(){
    int ret = 0,fl = 1;
    char c = getchar();
    for(;!isdigit(c)&&c != '-';c = getchar())if(c == '-')fl = 0;
    for(;isdigit(c);c = getchar())ret = (ret << 3) + (ret << 1) + c - 48;
    return fl ? ret : -ret;
}

#define ls tr[x].Ls
#define rs tr[x].Rs
inline int findf(int x){return tr[x].rt == x ? x : tr[x].rt = findf(tr[x].rt);}

inline int Merge(int x,int y){
    if(!x || !y)return x + y;
    if(tr[x].val > tr[y].val)swap(x,y);
    if(tr[x].val == tr[y].val && x > y)swap(x,y);
    rs = Merge(rs,y);
    if(tr[ls].dis < tr[rs].dis)swap(ls,rs);
    tr[ls].rt = tr[rs].rt = tr[x].rt = x;
    tr[x].dis = tr[rs].dis + 1;
    return x;
}

inline void Pop(int x){
    tr[x].val = -1;
    tr[ls].rt = ls;tr[rs].rt = rs;
    tr[x].rt = Merge(ls,rs);
}

int main(){
//  freopen("testdata.in","r",stdin);
//  freopen("testdata.out","w",stdout);

    N = read();M = read();tr[0].dis = -1;
    for(register int i = 1;i <= N;++i)
        tr[i].val = read(),tr[i].rt = i;
    for(register int i = 1;i <= M;++i){
        int op = read();
        if(op == 1){
            int x = read();int y = read();
            if(tr[x].val == -1||tr[y].val == -1)continue;
            x = findf(x);y = findf(y);
            if(x == y)continue;
            tr[x].rt = tr[y].rt = Merge(x,y);
        }
        else{
            int x = read();
            if(tr[x].val == -1)printf("-1\n");
            else{
                x = findf(x);
                printf("%d\n",tr[x].val);Pop(x);
            }
        }
    }
    return 0;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容

  • 2.1.2 可并堆的定义 可并堆(Mergeable Heap)也是一种抽象数据类型,它除了支持优先队列的三个基本...
    Gitfan阅读 1,353评论 0 0
  • 0.目录 1.优先队列ADT 2.几种实现 3.二叉堆 4.d-堆 5.左式堆 6.斜堆 7.二项队列 8.斐波那...
    王侦阅读 3,085评论 1 2
  • 一些概念 数据结构就是研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算,而且确保经过这...
    Winterfell_Z阅读 5,767评论 0 13
  • 今天晚上是除夕,今天也是我最高兴的一天,我终于把我的简书账号和密码找到了。 从今天起,我又可...
    黑白尘埃阅读 156评论 0 0
  • 今天上午儿子学电子琴,本来昨天下午要学的,龙都客栈有参加高考的考生住,取消了。 十点接回来时,图图问:妈妈,你还考...
    纶纶妈阅读 316评论 2 3