題意:N個布丁擺成一行,進行M次操作.每次將某個顏色的布丁全部變成另一種顏色的,然後再詢問當前一共有多少段顏色。
題解:啓發式合併的神奇做法
把同種顏色的布丁排成一列,變色時接在那個顏色的隊列後面;同時要把短的隊列接在長的隊列後面。
複雜度證明:由於每個操作中,合併短的隊列和長的隊列,合併後的隊列至少有短的隊列的2倍長
最多擴大 次,複雜度 。
那麼問題來了:如果改變顏色時,原來顏色的隊列比改變顏色的隊列長怎麼辦呢?我們可以記錄一個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;
}