# 左偏树

ACM-与堆有关的算法 专栏收录该内容
2 篇文章 0 订阅

## 模板：

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int MAX = 1e5 + 10;

//左偏树结构体定义
struct tree {
int l, r;
int fa, dis, val;
}tr[MAX];

int n, m;

//并查集
int finds(int x)
{
return tr[x].fa == -1 ? x : tr[x].fa = finds(tr[x].fa);
}

//合并
//插入操作相当于一个节点的左偏树和另一棵左偏树进行合并
int merge(int x, int y)
{
if (x == 0 || y == 0)	return x + y;  //如果为0的话，就说明是空子树，根节点当然就是另一节点了
if (tr[y].val > tr[x].val)  swap(x, y);  //始终往右子树进行插入
tr[x].r = merge(tr[x].r, y);
tr[tr[x].r].fa = x;
if (tr[tr[x].l].dis < tr[tr[x].r].dis) swap(tr[x].l, tr[x].r);   //是否需要左右子树的对换，这样是为了右子树尽量短
if (tr[x].r == 0)  tr[x].dis = 0;   //距离的重新分配
else tr[x].dis = tr[tr[x].r].dis + 1;
return x;
}

//删除
int del(int root)
{
int l = tr[root].l;
int r = tr[root].r;
tr[root].l = tr[root].r = tr[root].dis = 0;
tr[root].fa = -1;
tr[l].fa = tr[r].fa = -1;  //删除root根节点
return merge(l, r);       //这样一来相当于分裂成了两棵子树，重新进行合并，最后返回值为合并后的根节点
}

//初始化
void init()
{
for (int i = 1; i <= n; i++) {
tr[i].l = tr[i].r = tr[i].dis = 0;
tr[i].fa = -1;
}
}


## 例题：

hdu 1512 Monkey King

### Code：

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int MAX = 1e5 + 10;

struct tree {
int l, r;
int fa, dis, val;
}tr[MAX];

int n, m;

int finds(int x)
{
return tr[x].fa == -1 ? x : tr[x].fa = finds(tr[x].fa);
}

int merge(int x, int y)
{
if (x == 0 || y == 0)	return x + y;
if (tr[y].val > tr[x].val)  swap(x, y);
tr[x].r = merge(tr[x].r, y);
tr[tr[x].r].fa = x;
if (tr[tr[x].l].dis < tr[tr[x].r].dis) swap(tr[x].l, tr[x].r);
if (tr[x].r == 0)  tr[x].dis = 0;
else tr[x].dis = tr[tr[x].r].dis + 1;
return x;
}

int del(int root)
{
int l = tr[root].l;
int r = tr[root].r;
tr[root].l = tr[root].r = tr[root].dis = 0;
tr[root].fa = -1;
tr[l].fa = tr[r].fa = -1;
return merge(l, r);
}

int main()
{
while (scanf("%d", &n) != EOF)
{
for (int i = 1; i <= n; i++) {
tr[i].l = tr[i].r = tr[i].dis = 0;
tr[i].fa = -1;
scanf("%d", &tr[i].val);
}
scanf("%d", &m);
while (m--) {
int x, y;
scanf("%d%d", &x, &y);
int fx = finds(x);
int fy = finds(y);
if (fx == fy) {
printf("-1\n");
continue;
}
tr[fx].val /= 2;
//插入操作相当于一个节点的左偏树和另一棵左偏树进行合并
int xx = merge(del(fx), fx);
tr[fy].val /= 2;
int yy = merge(del(fy), fy);
printf("%d\n", tr[merge(xx, yy)].val);
}
}
return 0;
}


• 0
点赞
• 0
评论
• 3
收藏
• 一键三连
• 扫一扫，分享海报

07-28

06-07 270
01-20 197
09-04 72
09-27 3042
01-10 41
08-22 58
08-13 157
05-27 323
02-15 1501
10-06 982
09-03 26
©️2021 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客

1.余额是钱包充值的虚拟货币，按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载，可以购买VIP、C币套餐、付费专栏及课程。