传送门:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1483
什么是启发式合并,我的理解是将小的合并进大的,达到复杂度为nlogn。
而这道题,我们把颜色相同的块用链表链起来,而每次颜色改变的时候,就把小的链表暴力改变,然后链到大的里,再表头记录颜色即可。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 1000010; int fir[maxn], next[maxn], edge[maxn]; int a[maxn], fi[maxn], sz[maxn]; int n, m, ans, cnt; void get(int &tmp) { tmp = 0; char ch = getchar(); for(; ch < '0' || ch > '9'; ch = getchar()); for(; ch >= '0' && ch <= '9'; ch = getchar()) tmp = tmp * 10 + ch - '0'; return; } void addedge(int a, int b) { edge[cnt] = b; next[cnt] = fir[a]; fir[a] = cnt ++; return; } int main() { memset(fir, -1, sizeof(fir)); get(n); get(m); for(int i = 1; i < maxn; i ++) fi[i] = i; for(int i = 1; i <= n; i ++) { get(a[i]); if(a[i] != a[i - 1]) ans ++; addedge(a[i], i); sz[a[i]] ++; } int k, x, y; for(int i = 1; i <= m; i ++) { get(k); if(k == 2) printf("%d\n", ans); else { get(x); get(y); if(x == y) continue; if(sz[fi[x]] > sz[fi[y]]) swap(fi[x], fi[y]); x = fi[x]; y = fi[y]; if(!sz[x] || !sz[y]) continue; for(int j = fir[x]; ~j ; j = next[j]) { int v = edge[j]; if(a[v] != a[v - 1]) ans --; if(v < n && a[v] != a[v + 1]) ans --; } for(int j = fir[x]; ~j ; j = next[j]) { int v = edge[j]; a[v] = y; } int last = 0; for(int j = fir[x]; ~j ; j = next[j]) { last = j; int v = edge[j]; if(a[v] != a[v - 1]) ans ++; if(v < n && a[v] != a[v + 1]) ans ++; } sz[y] += sz[x]; sz[x] = 0; next[last] = fir[y]; fir[y] = fir[x]; fir[x] = -1; } } return 0; }