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 }
题目大意:N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色.
做法:我们考虑链表的启发式合并,每次我们把数量少的往数量大的合并即可,还要注意,每次小的往的合并时可能会导致有些颜色应该有而链表中却没有了,所以我们需要记录每种颜色在链表中实际上是什么颜色。为了保证链表合并的复杂度,我们对每条链记录首尾端点,链表的合并就是O(1)了,而颜色的修改均摊是logn的,所以总复杂度为O(mlogn)。
链表+启发式合并。