感觉本题与梦幻布丁(题解)十分相似。只不过由全局查找变为区间查找。若是此时对于每种颜色都查询一遍区间内颜色段数复杂度必定超。但由梦幻布丁这道题做铺垫后,可以想到维护一个区间内所有颜色的段数,可以另外拿一个线段树来维护,查询时在这个线段树上回答即可。
为了快速确定某两个相邻的点是否颜色相同,可以用并查集。把每种颜色的代表点存下来(也就是随便存下一个这种颜色的点的位置),每次合并时将两个代表合并即可。
#include
#include
#include
#define M 100005
using namespace std;
int C[M];
map<int,int>mp;
int fa[M],Fr[M];
int getfa(int v){
if(fa[v]==v)return v;
return fa[v]=getfa(fa[v]);
}
struct SegmentTree{
int Root[M],tot;
int Lson[M*20],Rson[M*20],cnt[M*20],sum[M*20];//一个log
bool Lmark[M*20],Rmark[M*20];
void Init(){
tot=0;
memset(Root,0,sizeof(Root));
memset(sum,0,sizeof(sum));
}
void Clear(int tid){//建一个节点要先清空
Lson[tid]=Rson[tid]=0;
cnt[tid]=0;
Lmark[tid]=Rmark[tid]=0;
}
void Up(int p){
Lmark[p]=Lmark[Lson[p]];
Rmark[p]=Rmark[Rson[p]];
cnt[p]=cnt[Lson[p]]+cnt[Rson[p]]-(Rmark[Lson[p]]&&Lmark[Rson[p]]);
}
void Updata(int L,int R,int x,int &tid,int p){//此处的p时线段树节点编号
if(!tid)tid=++tot,Clear(tid);
sum[p]-=cnt[tid];//先减去
if(L==R){
cnt[tid]=1;
sum[p]++;//注意在此时不要忘了更新sum数组
Lmark[tid]=Rmark[tid]=1;
return;
}
int mid=(L+R)>>1;
if(x<=mid)Updata(L,mid,x,Lson[tid],p<<1);
else Updata(mid+1,R,x,Rson[tid],p<<1|1);
Up(tid);
sum[p]+=cnt[tid];//更新
}
int Merge(int x,int y,int p){
if(!x)return y;
if(!y)return x;
sum[p]-=cnt[y]+cnt[x];//合并时也要维护sum数组
Lson[y]=Merge(Lson[x],Lson[y],p<<1);
Rson[y]=Merge(Rson[x],Rson[y],p<<1|1);
Up(y);
sum[p]+=cnt[y];
return y;
}
void Union(int x,int y){
Root[y]=Merge(Root[x],Root[y],1);
}
int Query(int L,int R,int Lx,int Rx,int p){//类似在线段树上查询
if(Lx>Rx)return 0;
if(Lx<=L&&R<=Rx)return sum[p];
int mid=(L+R)>>1;
if(Rx<=mid)return Query(L,mid,Lx,Rx,p<<1);
if(Lx>mid)return Query(mid+1,R,Lx,Rx,p<<1|1);
return Query(L,mid,Lx,mid,p<<1)+Query(mid+1,R,mid+1,Rx,p<<1|1)-(getfa(mid)==getfa(mid+1));
}
}ST;
int main(){
int T;
scanf("%d",&T);
while(T--){
ST.Init();
mp.clear();
memset(Fr,0,sizeof(Fr));
int n,m,id=0;
scanf("%d%d",&n,&m);
for(int i=1;i<M;i++)fa[i]=i;//初始并查集
for(int i=1;i<=n;i++){
scanf("%d",&C[i]);
if(mp[C[i]]==0)mp[C[i]]=++id,Fr[mp[C[i]]]=i;
else fa[i]=Fr[mp[C[i]]];
}
for(int i=1;i<=n;i++)ST.Updata(1,n,i,ST.Root[mp[C[i]]],1);
while(m--){
int op,x,y;
scanf("%d%d%d",&op,&x,&y);
if(op==1){
if(x==y)continue;
int a=mp[x],b=mp[y];
mp[x]=0;
if(a!=0&&b!=0)fa[Fr[a]]=Fr[b],Fr[a]=0;//两个代表之间的合并,主要要清空被合并的a,注意前面if语句的条件
if(b!=0)ST.Union(a,b);
else mp[y]=a;
}else {
printf("%d\n",ST.Query(1,n,x,y,1));
}
}
}
return 0;
}