[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
 */

原文地址:https://www.cnblogs.com/sshwy/p/10999419.html