「学习笔记」启发式合并

一. 简介

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

二.算法介绍:

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

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

三.例题讲解

P3201 [HNOI2009] 梦幻布丁

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

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

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

代码如下:

#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 @ 2022-02-10 10:28  cyhyyds  阅读(358)  评论(0编辑  收藏  举报