「学习笔记」启发式合并

一. 简介#

启发式合并是一种合并方法,它通过两两合并的方法将 n 个元素的信息合并到一起。时间复杂度是 O(nlogn)。这种方法被广泛运用在各种数据结构中。

二.算法介绍:#

方法:合并两个集合 s1,s2 时,若 |s1|<|s2|,则将 s1 中的元素加入 s2

复杂度分析:
考虑贡献发。合并两个集合时,若元素属于较小的集合,那么它会被合并到较大的集合中。若一个元素被转移,则它所处的集合的大小至少变成了原来的两倍。假设集合大小的上限是 n,则一个元素最多被转移 logn 次,n 个元素最多被转移 nlogn 次,所以启发式合并的时间复杂度是 O(nlogn) 的。

三.例题讲解#

P3201 [HNOI2009] 梦幻布丁#

对于此题,先求出原序列的答案,每一种颜色都用类似链表的数据结构存储起来,并记录下结束节点。每次修改都根据启发式合并的方式来暴力合并,然后处理这次合并对于答案的影响。(答案是不增的)

如果我们将 1 染成 2 并且 |s1|>|s2|,那么我们应该将 2 接到 1 的后面。这里有一个问题:这次修改后这个链的颜色是 1 (颜色为 2 的链被删除了),如果接下来修改颜色 2,会因为找不到颜色 2 而只能找到颜色 1。所以我们需要用一个 f 数组,表示当我们需要寻找颜色 x时,只需要寻找颜色为 fx 的链。也就是说,遇到上面这种情况需要交换 f1f2

查询时,在输入时已经求好了原序列的答案,当某个元素变为颜色 B 时,左边的元素为 A,右边的元素为 C 时,答案分三种情况:
1. ABC,那么答案不变;
2. A=BC,那么答案减一;
3. AB=C,那么答案减一。

代码如下:

Copy
#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; }
posted @   cyhyyds  阅读(360)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
CONTENTS