#啟發式合併,連結串列#洛谷 3201 [HNOI2009] 夢幻布丁
阿新 • • 發佈:2020-08-19
題目
\(n\)個布丁擺成一行,進行\(m\)次操作。
每次將某個顏色的布丁全部變成另一種顏色的,
然後再詢問當前一共有多少段顏色。
(\(n,m\leq 10^5,col\leq 10^6\))
分析
考慮用連結串列儲存每一種顏色的位置,由於顏色總數只會減少不會增多,
考慮啟發式合併,將個數小的合併到個數大的,並交換實際的顏色表示,
時間複雜度\(O(nlog_2n)\)
程式碼
#include <cstdio> #include <cctype> #define rr register using namespace std; const int N=1000011; int ls[N],st[N],cnt[N],col[N/10],f[N],nxt[N/10],n,m,ans; inline signed iut(){ rr int ans=0; rr char c=getchar(); while (!isdigit(c)) c=getchar(); while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar(); return ans; } inline void print(int ans){ if (ans>9) print(ans/10); putchar(ans%10+48); } inline void Turn_Into(int x,int y){ for (rr int i=ls[x];i;i=nxt[i]) ans-=(col[i-1]==y)+(col[i+1]==y);//如果相鄰塊數減小 for (rr int i=ls[x];i;i=nxt[i]) col[i]=y;//更改顏色 nxt[st[x]]=ls[y],ls[y]=ls[x],cnt[y]+=cnt[x],ls[x]=st[x]=cnt[x]=0;//更新連結串列 } signed main(){ n=iut(); m=iut(); for (rr int i=1;i<=n;++i){ col[i]=iut(),ans+=col[i]!=col[i-1]; if (!ls[col[i]]) st[col[i]]=i,f[col[i]]=col[i]; ++cnt[col[i]],nxt[i]=ls[col[i]],ls[col[i]]=i; } while (m--){ rr int opt=iut(); if (opt==2) print(ans),putchar(10); else{ rr int x=iut(),y=iut(); if (x==y) continue;//顏色相同不需要合併 if (cnt[f[x]]>cnt[f[y]]) f[x]^=f[y],f[y]^=f[x],f[x]^=f[y];//個數小的合併到個數大的 if (!cnt[f[x]]) continue;//不需要合併 Turn_Into(f[x],f[y]); } } return 0; }