HNOI2009 夢幻布丁 啓發式合併+隊列

傳送門

題意:N個布丁擺成一行,進行M次操作.每次將某個顏色的布丁全部變成另一種顏色的,然後再詢問當前一共有多少段顏色。

題解:啓發式合併的神奇做法
把同種顏色的布丁排成一列,變色時接在那個顏色的隊列後面;同時要把短的隊列接在長的隊列後面。
複雜度證明:由於每個操作中,合併短的隊列和長的隊列,合併後的隊列至少有短的隊列的2倍長
最多擴大logn 次,複雜度O(nlogn)
那麼問題來了:如果改變顏色時,原來顏色的隊列比改變顏色的隊列長怎麼辦呢?我們可以記錄一個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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章