「学习笔记」启发式合并
一. 简介#
启发式合并是一种合并方法,它通过两两合并的方法将 个元素的信息合并到一起。时间复杂度是 。这种方法被广泛运用在各种数据结构中。
二.算法介绍:#
方法:合并两个集合 时,若 ,则将 中的元素加入 。
复杂度分析:
考虑贡献发。合并两个集合时,若元素属于较小的集合,那么它会被合并到较大的集合中。若一个元素被转移,则它所处的集合的大小至少变成了原来的两倍。假设集合大小的上限是 ,则一个元素最多被转移 次, 个元素最多被转移 次,所以启发式合并的时间复杂度是 的。
三.例题讲解#
P3201 [HNOI2009] 梦幻布丁#
对于此题,先求出原序列的答案,每一种颜色都用类似链表的数据结构存储起来,并记录下结束节点。每次修改都根据启发式合并的方式来暴力合并,然后处理这次合并对于答案的影响。(答案是不增的)
如果我们将 染成 并且 ,那么我们应该将 接到 的后面。这里有一个问题:这次修改后这个链的颜色是 (颜色为 的链被删除了),如果接下来修改颜色 ,会因为找不到颜色 而只能找到颜色 。所以我们需要用一个 数组,表示当我们需要寻找颜色 时,只需要寻找颜色为 的链。也就是说,遇到上面这种情况需要交换 和 。
查询时,在输入时已经求好了原序列的答案,当某个元素变为颜色 时,左边的元素为 ,右边的元素为 时,答案分三种情况:
,那么答案不变;
,那么答案减一;
,那么答案减一。
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 7;
int n, m, col[N], siz[N], head[N], nxt[N], f[N], res = 0, op, x, y;
inline void add_edge (int x, int i) {
nxt[i] = head[x];
head[x] = i;
siz[x] ++;
}
inline void merge (int &x, int &y) {
if (siz[x] > siz[y]) {
swap (x, y);
}
if (!siz[x] || x == y) {
return ;
}
for (int i = head[x]; i != -1; i = nxt[i]) {
if (col[i - 1] == y) {
res --;
}
if (col[i + 1] == y) {
res --;
}
}
for (int i = head[x]; i != -1; i = nxt[i]) {
col[i] = y;//直接修改
if (nxt[i] == -1) {
nxt[i] = head[y];
head[y] = head[x];
break;//合并
}
}
siz[y] += siz[x], siz[x] = 0, head[x] = -1;
}
int main () {
cin >> n >> m;
for (int i = 1; i < N; i ++) {
head[i] = -1;
f[i] = i;
}
for (int i = 1; i <= n; i ++) {
cin >> col[i];
int x = col[i];
add_edge (x, i);//前向星记录
if (nxt[i] != i - 1) {
res ++;//计算答案
}
}
for (int i = 1; i <= m; i ++) {
cin >> op;
if (op == 1) {
cin >> x >> y;
merge (f[x], f[y]);
}
else if (op == 2) {
cout << res << endl;
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!