【BZOJ1483】【HNOI2009】梦幻布丁
题意:n个连续的点,有若干种颜色,每个颜色会因为某些操作变为另一种颜色,动态查询颜色段数。
解题思路:对每个颜色开一棵平衡树启发式合并应该是最裸的想法,但是我们有更优的!
考虑对每个颜色利用链表储存它的集合,在合并两种颜色时可以很简单通过对比原节点位置的前后颜色来进行答案的更新,然后利用启发式合并进行保证合并效率即可。
总复杂度: \( O( q \log c ) \) / \( O (c) \) C表示颜色数。(目前是BZOJ Rank2 200ms)
#include <stdio.h> #define MN 100005 #define MC 1000005 #define r register #define getchar() (S==TT&&(TT=(S=BB)+fread(BB,1,1<<15,stdin),TT==S)?EOF:*S++) char BB[1<<15],*S=BB,*TT=BB; inline int read(){ r int x=0;r char c; for (;(c=getchar())<'0'||c>'9';); for (x=c-'0';(c=getchar())>='0'&&c<='9';x=(x<<3)+(x<<1)+c-'0'); return x; } inline void swap(int &a,int &b){a^=b^=a^=b;} int rt[MC],nxt[MN],s[MC],col[MN],n,q,ans; inline void merge(int x,int y){ if (s[x]>s[y]){swap(x,y);} for (r int i=rt[x]; i; i=nxt[i]){ ans-=(col[i-1]==y)+(col[i+1]==y); if (!nxt[i]){nxt[i]=rt[y];break;} }for (r int i=rt[x]; i!=rt[y]; i=nxt[i]) col[i]=y; rt[y]=rt[x];s[y]+=s[x];s[x]=rt[x]=0; } void init(){ n=read(),q=read();ans=1; for (int i=1; i<=n; ++i) col[i]=read(),nxt[i]=rt[col[i]],rt[col[i]]=i; for (r int i=2; i<=n; ++i) if (col[i]!=col[i-1]) ++ans; } void solve(){ while(q--){ r int op=read(); if (op==1){r int x=read(),y=read();if (x!=y) merge(x,y);} else printf("%d\n",ans); } } int main(){init();solve();return 0;}