洛谷P3201 [HNOI2009] 夢幻布丁(鏈式儲存+啟發式合併)
阿新 • • 發佈:2020-10-29
鏈式儲存後按題意模擬就行
#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; }