启发式合并板子(梦幻布丁)

发布时间 2023-03-25 16:36:23作者: _Codjjj

Link

启发式合并是针对n个集合(总元素个数是O(n))的合并操作,每次将小的集合合并到大的集合

复杂度证明:

考虑每一个元素$$e \in E$$的贡献,如果在某一次合并中该元素被移动,那么集合的大小至少是$$2|E|$$,故复杂度是$$O(nlogn)$$

具体的题目而言,我们可以看出对于$$x \to y$$而言,具体是把x变成y,还是把y变成x,其实对结果并不重要,但我们对于每个颜色它的位置的集合需要维护

pos[x]是x颜色(真实的颜色)所在的位置集合

每次将$$x \to y$$ ,如果$$size[x]>size[y]$$将x,y互换(复杂度是O(1)的)

再将pos[x]并到pos[y]上,这样每一个颜色的位置集合都被正确的维护了,至于每个集合pos[x]中的位置存的颜色是否真的是x,对结果并不重要

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 1e5+10,M=1e6+10;
int n, m, k, T;
int c[N];
vector<int> pos[M];
int main()
{
	scanf("%d%d",&n,&m);
	int seg=0;
	for (int i = 1; i <= n; i++) {
		scanf("%d", &c[i]);
		pos[c[i]].push_back(i);
		
	}
	for(int i=1;i<=n;i++)
		seg+=(c[i]!=c[i-1]);
	while (m--) {
		int op;
		scanf("%d",&op);
		if (op == 1) {
			int x,y;
			scanf("%d%d",&x,&y);
			if(x==y)continue;

			if (pos[x].size() > pos[y].size()) {
				pos[x].swap(pos[y]);//!!!!!!
			}
			if(!pos[y].size())continue;
			int clr=c[pos[y][0]];
			for (auto e : pos[x]) {
				seg+=-(c[e]!=c[e-1])-(c[e]!=c[e+1])+(clr!=c[e-1])+(clr!=c[e+1]);
				c[e]=clr;
				pos[y].push_back(e);
			}
			pos[x].clear();
			
		}
		else {
			printf("%d\n",seg);
		}
	}

}