传送门: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;
}