Link

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

复杂度证明:

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

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

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

每次将xy ,如果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);
}
}
}