Link
题目描述
解法
每种颜色的点都可以构成一个序列,而把一种颜色改成另一种颜色,实际上是将一个序列接在另一个序列后面,同时将段数量维护一下。段数量如何维护?可以看出,对于每次修改,只有被修改的点可能会产生贡献,且只与这些点前一个和后一个点有关。如果修改了之后前后的点颜色相同,就将段数减一。事实上将 (x) 并到 (y) 和将 (y) 并到 (x) 是等价的,所以每次可以贪心地选择将少的合并到多的上面,即启发式合并。而对于这个序列,用一个链表维护就可以了。每次合并,只需要修改头指针和尾指针,并将节点少的那个链表遍历一遍,暴力修改原序列颜色。这样复杂度是 (O(nlog n)) 的。
Tips
题目要求将 (x) 改成 (y),但我们有时候将 (y) 改成了 (x)。所以还要维护一个 (ref[x]),表示颜色 (x) 在当前意义下指代的是哪个颜色。
#include<stdio.h>
#define N 1000007
inline int read(){
int x=0,flag=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
inline void swap(int &x,int &y){x^=y,y^=x,x^=y;}
int n,m,h[N],t[N],ans=0,nxt[N],cnt=0,sz[N],a[N],ref[N];
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++){
a[i]=read(),ref[a[i]]=a[i];
if(a[i]!=a[i-1]) ans++;
if(!t[a[i]]) h[a[i]]=i;
sz[a[i]]++,nxt[i]=t[a[i]],t[a[i]]=i;
}
while(m--){
int opt=read();
if(opt==1){
int x=read(),y=read();
if(x==y) continue;
if(sz[ref[x]]>sz[ref[y]])
swap(ref[x],ref[y]);
x=ref[x],y=ref[y];
if(!sz[x]) continue;
for(int i=t[x];i;i=nxt[i])
ans-=(a[i-1]==y)+(a[i+1]==y);
for(int i=t[x];i;i=nxt[i]) a[i]=y;
nxt[h[x]]=t[y],sz[y]+=sz[x],t[y]=t[x];
t[x]=h[x]=sz[x]=0;
}else printf("%d
",ans);
}
}