bzoj1483: [HNOI2009]梦幻布丁

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5 using namespace std;
 6 #define maxn 1000005
 7 int n,m,ans,fa[maxn],color[maxn],st[maxn],now[maxn],prep[maxn],size[maxn];
 8 void read(int &x){
 9     x=0; int f=1; char ch;
10     for (ch=getchar();!isdigit(ch);ch=getchar()) if (ch=='-') f=-1;
11     for (;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; x*=f;
12 }
13 void merge(int x,int y){
14     if (size[fa[x]]>size[fa[y]]) swap(fa[x],fa[y]);
15     if (size[fa[x]]==0) return;
16     x=fa[x],y=fa[y];
17     for (int i=now[x];i;i=prep[i]){
18         if (color[i-1]==y) ans--;
19         if (color[i+1]==y) ans--;
20     }
21     for (int i=now[x];i;i=prep[i]) color[i]=y;
22     size[y]+=size[x],size[x]=0;
23     prep[st[x]]=now[y],now[y]=now[x]; now[x]=st[x]=size[x]=0;
24 }
25 int main(){
26     read(n),read(m); ans=0;
27     for (int i=1;i<=n;i++){
28         read(color[i]); size[color[i]]++,fa[color[i]]=color[i];
29         if (color[i]!=color[i-1]) ans++;
30         if (!st[color[i]]) st[color[i]]=i;
31         prep[i]=now[color[i]],now[color[i]]=i;
32     }
33     for (int type,x,y;m;--m){
34         read(type);
35         if (type==2) printf("%d\n",ans);
36         else{
37             read(x),read(y);if (x==y) continue;
38             merge(x,y);
39         }
40     }
41     return 0;
42 }
View Code

题目大意:N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色.

 做法:我们考虑链表的启发式合并,每次我们把数量少的往数量大的合并即可,还要注意,每次小的往的合并时可能会导致有些颜色应该有而链表中却没有了,所以我们需要记录每种颜色在链表中实际上是什么颜色。为了保证链表合并的复杂度,我们对每条链记录首尾端点,链表的合并就是O(1)了,而颜色的修改均摊是logn的,所以总复杂度为O(mlogn)。

链表+启发式合并。

posted @ 2016-06-26 11:45  oyzx~  阅读(179)  评论(0编辑  收藏  举报