题意:N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色。
题解:启发式合并的神奇做法
把同种颜色的布丁排成一列,变色时接在那个颜色的队列后面;同时要把短的队列接在长的队列后面。
复杂度证明:由于每个操作中,合并短的队列和长的队列,合并后的队列至少有短的队列的2倍长
最多扩大
logn
l
o
g
n
次,复杂度
O(nlogn)
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;
}