洛谷P3201 [HNOI2009] 梦幻布丁(链式存储+启发式合并)

链式存储后按题意模拟就行

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e3;
inline int read(){
	int ret=0;
	int f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')
			f=-f;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		ret=ret*10+(ch^'0');
		ch=getchar();
	}
	return f*ret;
}
int n,m;
int c[maxn];
int head[maxn];
int nex[maxn];
int fa[maxn];
int sz[maxn];
int st[maxn];
int ans;
int x,xx,y,yy;
void add(int a,int b){
	for(int i=head[a];i;i=nex[i]){
		ans-=(c[i-1]==b)+(c[i+1]==b);
	}
	for(int i=head[a];i;i=nex[i]){
		c[i]=b;
	}
	nex[st[a]]=head[b];
	head[b]=head[a];
	sz[b]+=sz[a];
	st[a]=sz[x]=head[a]=0;
	return ;
}
int main(){
		n=read();
		m=read();
		for(int i=1;i<=n;i++){
			c[i]=read();
			ans+=(c[i]!=c[i-1]);
			fa[c[i]]=c[i];
			/*为什么我们要设置fa数组?
假设有1,2两种颜色,显然将2全变为1和将1全变为2对答案的贡献是同样的我们考虑到对时间复杂度的优化
所以对合并采取的是启发式合并,因此我们会将该颜色布丁个数较小的颜色染成较大的颜色
这时就引出了一个问题,假设存在1,2两种颜色,1颜色的布丁数量小于2,这时我们把2染成1,启发式合并导致我们实际把1染成2,假设下一个操作要对1进行,但我们已经把1变成2了,很明显是无法继续的.
这时我们便需要用到fa数组了,我们把fa数组初始化为每种颜色的编号,当每次启发式合并的合并顺序和当前所给不同时,我们便交换两种颜色的fa数组,我们让fa[1]=2,fa[2]=1这样每次对1,2,操作对fa进行操作。便可以避免出现上述情况。			
			*/
			if(!head[c[i]])
				st[c[i]]=i;
			sz[c[i]]++;
			nex[i]=head[c[i]];
			head[c[i]]=i;
		}
		int q;
		for(int i=1;i<=m;i++){
			q=read();
			if(q==2){
				cout<<ans<<endl;
			}
			else{
				x=read();
				y=read();
				if(fa[x]==fa[y]){
					continue;
				}
				if(sz[fa[x]]>sz[fa[y]]){
					swap(fa[x],fa[y]);//这一步交换非常重要。
				}
				if(!sz[fa[x]])
					continue;
				add(fa[x],fa[y]);				
			}
		}
	return 0;
}
原文地址:https://www.cnblogs.com/rpup/p/13895913.html