[HNOI2009]梦幻布丁
题目描述
N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色.
第一行给出N,M表示布丁的个数和好友的操作次数. 第二行N个数A1,A2...An表示第i个布丁的颜色从第三行起有M行,对于每个操作,若第一个数字是1表示要对颜色进行改变,其后的两个整数X,Y表示将所有颜色为X的变为Y,X可能等于Y. 若第一个数字为2表示要进行询问当前有多少段颜色,这时你应该输出一个整数.
Solution
膜了一发hzwer。
我们对于每一种颜色,开邻接表,把它搞成一个队列的形式,开一个数组记录它的开头。
对于一个修改,我们采用启发式合并的方式,可以把均摊复杂度将至llogn。
合并两个队列的方式就是把第一个队列接到第二个队列后面就可以了。
e[st[x]]=head[y];head[y]=head[x];就是这样
为了避免启发式合并之后颜色出现错乱if(!st[a[i]])st[a[i]]=i;if(size[ji[x]]>size[ji[y]])swap(ji[x],ji[y]);x=ji[x];y=ji[y];
Code
#include<iostream> #include<cstdio> #define NN 1000002 #define N 100002 using namespace std; int head[NN],ans,size[NN],e[N<<1],a[N],n,m,x,y,st[NN],ji[NN]; inline void solve(int x,int y){ for(int i=head[x];i;i=e[i]){ if(a[i+1]==y)ans--; if(a[i-1]==y)ans--; } for(int i=head[x];i;i=e[i])a[i]=y; size[y]+=size[x]; e[st[x]]=head[y];head[y]=head[x];size[x]=0; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i){ scanf("%d",&a[i]);ji[a[i]]=a[i]; if(!st[a[i]])st[a[i]]=i; if(a[i]!=a[i-1])ans++; e[i]=head[a[i]];head[a[i]]=i;size[a[i]]++; } for(int i=1;i<=m;++i){ scanf("%d",&x); if(x==2)printf("%d\n",ans); else{ scanf("%d%d",&x,&y); if(x==y)continue; if(size[ji[x]]>size[ji[y]])swap(ji[x],ji[y]); x=ji[x];y=ji[y]; if(size[x]==0)continue; solve(x,y); } } return 0; }