bzoj1483[HNOI2009]梦幻布丁
题意:
N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色。
题解:
给每个颜色建一个链表。先预处理出答案,然后每次修改颜色时将两个链表合并,同时将修改后颜色对答案的贡献重新计算(如果两个节点的位置相差大于1说明中间有一段颜色,将贡献+1)。链表合并时用启发式合并(就是将小的拆了依次插入到大的里面),复杂度O(nlog2n)(均摊复杂度,玄学)。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 #define M 1000010 6 #define N 100010 7 using namespace std; 8 9 int ans,head[M],last[N],next[N],comb[M],sz[M],n,m; 10 int main(){ 11 scanf("%d",&n); scanf("%d",&m); int a,b=0; ans=0; 12 memset(sz,0,sizeof(sz)); memset(comb,0,sizeof(comb)); memset(head,0,sizeof(head)); 13 inc(i,1,n){ 14 scanf("%d",&a); if(a!=b)ans++,comb[a]++; 15 next[i]=head[a]; last[head[a]]=i; head[a]=i; sz[a]++; b=a; 16 } 17 inc(i,1,m){ 18 int a1; scanf("%d",&a1); if(a1==2)printf("%d\n",ans); 19 if(a1==1){ 20 int a2,a3; scanf("%d%d",&a2,&a3); if(a2==a3)continue; 21 if(head[a2]==0)continue; 22 else if(head[a3]==0)head[a3]=head[a2],sz[a3]=sz[a2],comb[a3]=comb[a2],head[a2]=sz[a2]=comb[a2]=0; 23 else{ 24 if(sz[a2]<sz[a3]){ 25 int a4=head[a2],a6=head[a3]; 26 while(a4){ 27 int a5=next[a4]; 28 for(;a4<a6&&next[a6]>0;a6=next[a6]); 29 if(a4<a6)next[a6]=a4,last[a4]=a6,next[a4]=0; 30 else if(last[a6]==0)last[a4]=0,next[a4]=a6,last[a6]=a4,head[a3]=a4; 31 else next[last[a6]]=a4,last[a4]=last[a6],next[a4]=a6,last[a6]=a4; 32 sz[a3]++; a4=a5; 33 } 34 sz[a2]=head[a2]=0; 35 }else{ 36 int a4=head[a3],a6=head[a2]; 37 while(a4){ 38 int a5=next[a4]; 39 for(;a4<a6&&next[a6]>0;a6=next[a6]); 40 if(a4<a6)next[a6]=a4,last[a4]=a6,next[a4]=0; 41 else if(last[a6]==0)last[a4]=0,next[a4]=a6,last[a6]=a4,head[a2]=a4; 42 else next[last[a6]]=a4,last[a4]=last[a6],next[a4]=a6,last[a6]=a4; 43 sz[a2]++; a4=a5; 44 } 45 head[a3]=head[a2],sz[a3]=sz[a2],head[a2]=sz[a2]=0; 46 } 47 ans-=comb[a2]; ans-=comb[a3]; comb[a2]=comb[a3]=0; 48 for(int i=head[a3];i;i=next[i])if(i==head[a3]||i!=last[i]-1)comb[a3]++; 49 ans+=comb[a3]; 50 } 51 } 52 } 53 return 0; 54 }
20160320