Martian148's blog Martian148's blog
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档

Martian148

一只热爱文科的理科生
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档
  • 线上赛板子(实时更新)
  • 数据结构

    • 堆
      • 左偏树
        • 定义节点
        • 合并
        • 插入节点
        • 删除根
        • 整个堆加上/乘上一个值
        • 删除某个节点
    • 并查集
    • 树状数组
    • 分块
    • 树相关
    • 线段树
    • 平衡树
    • 树链剖分
    • LCT
    • 最近公共祖先
    • 虚树
    • 树分治
    • K-D Tree
    • 笛卡尔树
    • 珂朵莉树
  • 数学

  • 计算几何

  • 动态规划

  • 图论

  • 字符串

  • 杂项

  • 算法笔记
  • 数据结构
martian148
2024-09-28
目录

堆

# 堆

# 左偏树

左偏树是一种非常常见且好写的可并堆

image-20240913111902156

圆圈内部是结点的键值,首先满足堆的性质,所以键值肯定小于等于其儿子结点的键值(小根堆)

蓝色数字就是我们定义的一个距离 dist,定义为结点到外节点的距离,其中外界点为儿子数量小于 222 的节点,外界点的 dist 为 000

左偏树保证 左儿子的 dist ≥\ge≥ 右儿子的 dist

所以,很容易可以推出,一个节点的 dist === 右儿子的 dist +1+1+1

特别的空节点的距离为 −1-1−1

一个 nnn 个节点的左偏树根节点的 dist≤log⁡(n+1)−1dist \le \log(n+1)-1dist≤log(n+1)−1

# 定义节点

struct Node {
	int val, dist, rs, ls;
	Node() {val = rs = ls = 0;dist = -1;}
	Node(int val) : val(val), dist(0), rs(0), ls(0) {}
};
1
2
3
4
5

# 合并

合并操作是左偏树操作中最繁琐的一个,但它有点像 LCT 的 Access 操作

在合并过程中,我们需要维护好左偏树的性质,具体过程如下,我们设需要合并的两个堆的根节点记做 x,yx,yx,y 且 valx≤valyval_x\le val_yvalx​≤valy​

  • 根据堆的性质,所以合并完之后的根还是 xxx,递归合并 yyy 和 xxx 的右儿子
  • 由于合并完成之后可能破坏 xxx 的左偏性质,如果违反了则交换左右儿子
  • 然后更新 xxx 的 distdistdist ,为右儿子 +1+1+1
int merge (int x, int y) {
	if (x == 0 || y == 0) return x | y; // 如果有一个是空的,就返回另一个
	if (t[x].val > t[y].val) swap(x, y); // 保证x是较小的
	t[x].rs = merge(t[x].rs, y); // 将x的右儿子和y合并
	if (t[t[x].ls].dist < t[t[x].rs].dist) swap(t[x].ls, t[x].rs); // 保证左儿子的dist大于右儿子
	t[x].dist = t[t[x].rs].dist + 1; // 更新dist
	t[t[x].rs].fa = t[t[x].ls].fa = x;
	return x; // 返回合并后的根节点
}
1
2
3
4
5
6
7
8
9

# 插入节点

把单个节点看作一个堆,合并即可

# 删除根

合并根的左右儿子

# 整个堆加上/乘上一个值

和平衡树上打标记类似,堆上也可以打上标记,然后在遍历到的时候 pushdown 即可

# 删除某个节点

需要多维护一个节点的父亲

先合并这个节点的左右节点,然后更新这个节点的祖先节点

void push_up(int x) {
	if (!x) return ;
	if (t[x].dist != t[t[x].rs].dist + 1) {
		t[x].dist = t[t[x].rs].dist + 1;
		push_up(t[x].fa);
	}
}

void erase(int x) {
	int y = merge(t[x].ls, t[x].rs);
	t[y].fa = t[x].fa;
	if (t[t[x].fa].ls == x) t[t[x].fa].ls = y;
	else if (t[t[x].fa].rs == x) t[t[x].fa].rs = y;
	push_up(t[x].fa);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
上次更新: 2025/04/08, 18:03:31
线上赛板子(实时更新)
并查集

← 线上赛板子(实时更新) 并查集→

最近更新
01
Java基础语法
05-26
02
开发环境配置
05-26
03
pink 老师 JavaScript 学习笔记
05-26
更多文章>
Theme by Vdoing | Copyright © 2024-2025 Martian148 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式