Part 1
本题要求当前有多少段颜色,其实可以看成有多少个相同颜色的联通块,我们可以一个一个颜色进行计数。在颜色转换时利用线段树合并(归并思想)来维护当前有多少个相同颜色的联通块。
Part 2
对于每种颜色,可以用一颗动点线段树来维护其颜色段数,每个叶子节点存的就是这个节点是否为当前这种颜色。然后在Up操作里维护当前区间内这种颜色的段数。同时还要记录每个区间左端点以及右端点是否为这种颜色。具体解释可参考代码。
Part 3
由于给的颜色数据太大,顾可以用一个map将颜色的值离散化后再进行操作。有了map之后,颜色的变化也会变得简单,若颜色x变为颜色y,只用将两颗线段树合并后再将map内x所映射的值变为0即可。要注意一些特殊情况,如若y这种颜色不存在直接将y的映射值变为x。若x=y那么要跳过。
AC代码:
#include<cstdio>
#include<map>
#include<algorithm>
#define M 100005
using namespace std;
map<int,int>mp;
int B[M*21],id;
struct node{
int op,x,y;
void Read(){
scanf("%d",&op);
if(op==1)scanf("%d%d",&x,&y);
}
}Q[M*10];
int tot;
int C[M];
int Root[M*21];
int Lson[M*20],Rson[M*20],cnt[M*20];//1个log
bool Lmark[M*20],Rmark[M*20];//存这个区间的左/右端点是否为当前颜色
void Up(int L,int R,int p){
if(L==R)return;
Lmark[p]=Lmark[Lson[p]];
Rmark[p]=Rmark[Rson[p]];
cnt[p]=cnt[Lson[p]]+cnt[Rson[p]]-(Rmark[Lson[p]]&&Lmark[Rson[p]]);//若中间有相连要减1
}
void Updata(int L,int R,int x,int &tid){
if(!tid)tid=++tot;
if(L==R){
cnt[tid]=1;
Lmark[tid]=Rmark[tid]=1;
return;
}
int mid=(L+R)>>1;
if(x<=mid)Updata(L,mid,x,Lson[tid]);
else Updata(mid+1,R,x,Rson[tid]);
Up(L,R,tid);
}
int Merge(int L,int R,int x,int y){
if(!x)return y;
if(!y)return x;
int mid=(L+R)>>1;
Lson[y]=Merge(L,mid,Lson[x],Lson[y]);
Rson[y]=Merge(mid+1,R,Rson[x],Rson[y]);
Up(L,R,y);//注意要Up
return y;
}
int C_id;
int main(){
int n,m,ans=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&C[i]);
if(mp[C[i]]==0)mp[C[i]]=++C_id;//离散化
}
for(int i=1;i<=m;i++)Q[i].Read();
for(int i=1;i<=n;i++)Updata(1,n,i,Root[mp[C[i]]]);
for(int i=1;i<=C_id;i++)ans+=cnt[Root[i]];//初始值的统计
for(int i=1;i<=m;i++){
if(Q[i].op==1){
int x=mp[Q[i].x],y=mp[Q[i].y];
if(x==y)continue;//若两种颜色相等则跳过
mp[Q[i].x]=0;
if(y!=0){
int od,nw;
od=cnt[Root[x]]+cnt[Root[y]];
Root[y]=Merge(1,n,Root[x],Root[y]);
nw=cnt[Root[y]];
ans+=nw-od;//更新答案
}else mp[Q[i].y]=x;
}else printf("%d\n",ans);
}
return 0;
}