启发式合并训练之一

以前没怎没接触过,快要考试了,赶快来补一补

https://www.luogu.org/problem/P3201

分析:

将同一种颜色用类似链式前向星的形式存起来

考虑启发式合并,将小的合并到大的上面

如果要将x变为y,

如果sz[x]<sz[y],就直接把x接在y的上面就好

如果sz[x]>sz[y],如果直接把y接在x后面颜色就会变为x

所以我们需要使用一个 f数组,表示当我们要寻找颜色 x 时,实际上需要寻找颜色为 f[x] 的链。如果遇到上面这种情况就要交换交换 f[x]和 f[y]。

code :

#include<bits/stdc++.h>
#define ll long long
#define il inline
#define ri register int
#define lowbit(x) x&(-x)
using namespace std;
const int maxn=1e6+5;
int head[maxn],nxt[maxn],fir[maxn];
int a[maxn],col[maxn],sz[maxn];
int n,m,ans;
il void merge(int x,int y){
	for(ri i=head[x];i;i=nxt[i]){
		if(a[i-1]==y)ans--;
		if(a[i+1]==y)ans--;
	}
	for(ri i=head[x];i;i=nxt[i])a[i]=y;
	nxt[fir[x]]=head[y];
	head[y]=head[x]; 
	sz[y]+=sz[x];
	head[x]=sz[x]=fir[x]=0;
}
int main(){
	scanf("%d%d",&n,&m);
	for(ri i=1;i<=n;i++){
		scanf("%d",&a[i]);
		if(a[i]!=a[i-1])ans++;
	   col[a[i]]=a[i];
	   if(!head[a[i]])fir[a[i]]=i;
	   sz[a[i]]++;
	   nxt[i]=head[a[i]];
	   head[a[i]]=i;
	}
	while(m--){
		int op,x,y;
		scanf("%d",&op);
	   if(op==1){
		scanf("%d%d",&x,&y);
		if(x==y)continue;
		if(sz[col[x]]>sz[col[y]])swap(col[x],col[y]);
		if(!sz[col[x]])continue;
		merge(col[x],col[y]);
	}
	else printf("%d\n",ans);
	}
	return 0;
}

posted @ 2019-11-15 10:43  wzx_believer  阅读(124)  评论(0编辑  收藏  举报