luogu P3201 [HNOI2009]梦幻布丁 |线段树合并

题目描述

N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色.

输入格式

第一行给出N,M表示布丁的个数和好友的操作次数. 第二行N个数A1,A2...An表示第i个布丁的颜色从第三行起有M行,对于每个操作,若第一个数字是1表示要对颜色进行改变,其后的两个整数X,Y表示将所有颜色为X的变为Y,X可能等于Y. 若第一个数字为2表示要进行询问当前有多少段颜色,这时你应该输出一个整数. 0

输出格式

针对第二类操作即询问,依次输出当前有多少段颜色.


#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e6+5,M=6e6+5;
inline int read(){
	int x=0; char c=getchar();
	while(c<'0'||c>'9')c=getchar();
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
	return x;
}
int n,m;
int root[N],ls[M],rs[M],val[M],L[M],R[M],num;
#define mid ((l+r)>>1)
inline void pushup(int p){
	val[p]=val[ls[p]]+val[rs[p]]-(R[ls[p]]+1==L[rs[p]]);
	L[p]=L[ls[p]],R[p]=R[rs[p]];
}
void update(int &p,int l,int r,int pos,int d){
	if(!p)p=++num;
	if(l==r){ val[p]+=d; L[p]=R[p]=l; return; }
	if(pos<=mid)update(ls[p],l,mid,pos,d);
	else update(rs[p],mid+1,r,pos,d);
	pushup(p);
}
void merge(int &u,int v,int l,int r){
	if(!u||!v){ u|=v; return; }
	if(l==r){ val[u]=1; L[u]=R[u]=l;  return; }
	merge(ls[u],ls[v],l,mid);
	merge(rs[u],rs[v],mid+1,r);
	pushup(u);
}
signed main(){
	n=read(),m=read();
	int Max=0,ans=0;
	for(int i=1,x,lst=-1;i<=n;i++){
		x=read(); ans+=(lst!=x);
		lst=x; Max=max(Max,x);
		update(root[x],1,n,i,1);
	}
	int x,y;
	while(m--){
		x=read();
		if(x==1){
			x=read(),y=read();
			if(x==y)continue;
			ans-=val[root[x]]+val[root[y]];
			merge(root[y],root[x],1,n);
			ans+=val[root[y]];
			root[x]=0;
		}else printf("%d
",ans);
	}
}
原文地址:https://www.cnblogs.com/naruto-mzx/p/13038352.html