洛谷 P3201 [HNOI2009]梦幻布丁(启发式合并)

题面

luogu

题解

  • 什么是启发式合并?
    小的合并到大的上面
    复杂度\(O(nlogn)\)

这题颜色的修改,即是两个序列的合并

考虑记录每个序列的\(size\)

小的合并到大的

存序列用链表

但是有一种情况,
\(x->y\)
\(siz[x] > siz[y]\)

这个时候我们可以新建一个\(f\)数组,存一个真实颜色

碰到这种情况时,\(swap(f[x], f[y])\)

合并即是\(f[x]->f[y]\)

Code

// luogu-judger-enable-o2
#include<bits/stdc++.h>

#define LL long long
#define RG register

using namespace std;
template<class T> inline void read(T &x) {
    x = 0; RG char c = getchar(); bool f = 0;
    while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
    while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
    x = f ? -x : x;
    return ;
}
template<class T> inline void write(T x) {
    if (!x) {putchar(48);return ;}
    if (x < 0) x = -x, putchar('-');
    int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
    for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}

const int N = 1e6+10;

int c[N], ans;

int f[N], nxt[N], last[N], st[N], siz[N];

void merge(int x, int y) {//x合并到y上 
    for (int i = last[x]; i; i = nxt[i]) ans -= (c[i-1] == y) + (c[i+1] == y);
    for (int i = last[x]; i; i = nxt[i]) c[i] = y;
    nxt[st[x]] = last[y], last[y] = last[x], siz[y] += siz[x];
    last[x] = siz[x] = st[x] = 0;
}

int main() {
    int n, m;
    read(n), read(m);
    for (int i = 1; i <= n; i++) {
        read(c[i]);
        ans += c[i] != c[i-1];
        if (!last[c[i]]) st[c[i]] = i, f[c[i]] = c[i];
        nxt[i] = last[c[i]], last[c[i]] = i, siz[c[i]]++; 
    }
    while (m--) {
        int opt; read(opt);
        if (opt == 2) printf("%d\n", ans);
        else {
            int x, y;
            read(x), read(y);
            if (x == y) continue;
            if (siz[f[x]] > siz[f[y]]) swap(f[x], f[y]);
            if (!siz[f[x]]) continue;
            merge(f[x], f[y]); 
        }
    }
    return 0;
}

posted @ 2019-01-28 08:42  zzy2005  阅读(148)  评论(0编辑  收藏  举报