HNOI2009 梦幻布丁 启发式合并+队列

版权声明:本文为DyingShu原创文章,转载请注明出处哦。 https://blog.csdn.net/DyingShu/article/details/82054188

传送门

题意:N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色。

题解:启发式合并的神奇做法
把同种颜色的布丁排成一列,变色时接在那个颜色的队列后面;同时要把短的队列接在长的队列后面。
复杂度证明:由于每个操作中,合并短的队列和长的队列,合并后的队列至少有短的队列的2倍长
最多扩大 l o g n 次,复杂度 O ( n l o g n )
那么问题来了:如果改变颜色时,原来颜色的队列比改变颜色的队列长怎么办呢?我们可以记录一个Start数组,表示这个颜色实际上处于哪一队列。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 100001;


int a[MAXN], c[MAXN];
int Tail[1000001], Start[1000001], Size[1000001];
int fir[1000001], nxt[MAXN];

inline int read(){
    int k = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){k = k*10 + ch - '0'; ch = getchar();}
    return k * f;
}

int main(){
    freopen("in.txt", "r", stdin);
    int n = read(), m = read(), tot = 0;
    for(int i = 1; i <= n; i++){
        a[i] = read(); Start[a[i]] = a[i];
        if(a[i] != a[i - 1]) tot++;
        if(!fir[a[i]]) Tail[a[i]] = i;
        Size[a[i]]++;
        nxt[i] = fir[a[i]];
        fir[a[i]] = i;
    }
    while(m--){
        int opt = read();
        if(opt == 1){
            int x = read(), y = read();
            if(x == y) continue;
            if(Size[Start[x]] > Size[Start[y]]){
                swap(Start[x], Start[y]); //此时y接在x后面 
            }
            x = Start[x], y = Start[y]; //x接y上 
            if(!Size[x]) continue;
            for(int i = fir[x]; i; i = nxt[i]){
                if(a[i - 1] == y) tot--;
                if(a[i + 1] == y) tot--;
            }
            for(int i = fir[x]; i; i = nxt[i]) a[i] = y;
            nxt[Tail[x]] = fir[y]; fir[y] = fir[x]; Size[y] += Size[x];
            Tail[x] = Size[x] = fir[x] = 0;
        }
        else{
            printf("%d\n", tot);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/DyingShu/article/details/82054188