2019.7.8 義烏模擬賽 T2 B
阿新 • • 發佈:2021-07-09
我們發現每個值的貢獻其實是獨立的。
所以這啟發我們對於每個值單獨計算。
題目中真正有意義的合併只有\(O(n)\)次,每次暴力歸併所以是\(O(n^2+m)\)的。
但是這個顯然不夠優。
我們考慮啟發式合併。
這樣再用個set維護就可以了。時間複雜度\(O(nlog^2n)\)
用線段樹合併可以一隻log
code:
#include<bits/stdc++.h> #include<set> #define I inline #define re register #define Me(x,y) memset(x,y,sizeof(x)) #define N 300000 #define M 200000 #define W 200000 #define db double #define min(a,b) ((a)<(b)?(a):(b)) #define max(a,b) ((a)>(b)?(a):(b)) #define it iterator using namespace std; int n,m,siz[N+5],x,y,z,un,wn,l,r,mid,A[N+5],Maxn[N+5],fa[N+5],cnt,ans=2147483647;set<int> C[N+5];set<int>::it now,pus;map<int,int> G; I void Make(int &x){!G[x]&&(G[x]=++cnt);x=G[x];} I void swap(int &x,int &y){x^=y^=x^=y;} I void read(int &x){ char s=getchar();x=0;while(s<'0'||s>'9') s=getchar(); while(s>='0'&&s<='9') x=x*10+s-48,s=getchar(); } int main(){ freopen("b.in","r",stdin);freopen("b.out","w",stdout); re int i;scanf("%d%d",&n,&m);for(i=1;i<=n;i++) read(A[i]),Make(A[i]),C[A[i]].insert(i),siz[A[i]]++,Maxn[A[i]]&&(ans=min(ans,i-Maxn[A[i]])),Maxn[A[i]]=i; for(i=1;i<=n+2*m;i++) fa[i]=i; while(m--){ read(x);read(y);Make(x);Make(y);if(x==y) {printf("%d\n",ans);continue;} un=fa[x];wn=fa[y];if(siz[un]>siz[wn]) swap(un,wn);for(now=C[un].begin();now!=C[un].end();now++){ z=*now;pus=C[wn].lower_bound(z);if(pus!=C[wn].end()) ans=min(ans,(*pus)-z); if(pus!=C[wn].begin()) --pus,ans=min(ans,z-*pus); C[wn].insert(z); }C[un].clear();fa[x]=un;fa[y]=wn;siz[wn]+=siz[un];siz[un]=0;printf("%d\n",ans); } }