[HNOI2009]梦幻布丁
题意:N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色.
对每个颜色的位置维护链表。合并两个颜色,连接链表,统计贡献。
统计贡献的复杂度是与链表长度有关的。如果遍历长度短的链表那么复杂度自然更小(遍历链表然后比较相邻元素的颜色)
于是想到启发式合并,把小的合并到大的上
每个元素只多被合并log次(因为每次合并后长度翻一倍)总复杂度nlogn
#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e5+5,M=1e5+5,C=1e6+6;
int n,m;
int a[N];
int h[C],ed[C],nex[N],f[C],cnt[C];
int tot;
void merge(int x,int y){//将两个链表连接
for(int i=h[x];i;i=nex[i])tot-=(a[i-1]==y)+(a[i+1]==y);
for(int i=h[x];i;i=nex[i])a[i]=y;
nex[ed[x]]=h[y],h[y]=h[x],cnt[y]+=cnt[x];
h[x]=ed[x]=cnt[x]=0;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
tot+=a[i]!=a[i-1];
if(!h[a[i]])ed[a[i]]=i;
nex[i]=h[a[i]], h[a[i]]=i;
f[a[i]]=a[i], cnt[a[i]]++;
}
for(int i=1;i<=m;i++){
int op,x,y;
scanf("%d",&op);
if(op==1){
scanf("%d%d",&x,&y);
if(x==y)continue;
if(cnt[f[x]]>cnt[f[y]])swap(f[x],f[y]);//交换颜色
if(!cnt[f[x]])continue;//如果一个颜色不存在,那么就不用管了
merge(f[x],f[y]);
} else {
printf("%d
",tot);
}
}
return 0;
}
/*
* BUG#1: L12L13不能圧成一行
* BUG#2: L34没有用cnt
* BUG#3: 合并的时侯把x合并到了y后面,又没有更新ed[y]emmm
*/