启发式合并。
启发式合并就是每次将小的合并进大的里面。每次合并复杂度为O(n),因为每回大小都会翻倍,所以总复杂度就是O(nlogn)。
首先用链表维护每一种颜色。
询问直接输出答案。
否则合并(要记住,如果俩个其中一个是空的,直接特判,否则会浪费时间导致tle)。
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn = 100000 + 10; const int maxm = 1000000 + 10; const int INF = 0x3f3f3f3f; int size[maxm],g[maxm],last[maxm]; int next[maxn],a[maxn]; int n,m,x,y,cur,ans,op,end,r,tmp,tmp2; void build() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); size[a[i]]++; if(!g[a[i]]) g[a[i]]=i; else next[last[a[i]]]=i; last[a[i]]=i; if(a[i]!=a[i-1]) ans++; } } void merge(int x,int y) { size[y]+=size[x]; size[x]=0; next[0]=g[y]; next[last[y]]=INF; cur=g[x]; next[last[x]]=INF; for(int i=0;i<maxn;i=max(next[i],tmp2)) { tmp2=next[i]; if(cur<next[i]) { if(cur==i+1 && i) ans--; for(r=cur;next[r]<next[i]&&r!=last[x];r=next[r]); if(r+1==next[i]) ans--; tmp=next[r]; next[r]=next[i]; next[i]=cur; cur=tmp; } } if(r>last[y]) last[y]=r; g[y]=min(g[y],g[x]); } void change(int x,int y) { g[y]=g[x]; last[y]=last[x]; size[y]=size[x]; g[x]=last[x]=size[x]=0; } void solve() { while(m--) { scanf("%d",&op); if(op==2) printf("%d ",ans); else { scanf("%d%d",&x,&y); if(size[x]==0 || x==y) continue; else if(size[y]==0) change(x,y); else if(size[x]<=size[y]) merge(x,y); else { merge(y,x); change(x,y); } } } } int main() { build(); solve(); return 0; }