1. 程式人生 > 實用技巧 >洛谷P3201 [HNOI2009] 夢幻布丁(鏈式儲存+啟發式合併)

洛谷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;
}