2019.7.8 义乌模拟赛 T2 B
我们发现每个值的贡献其实是独立的。
所以这启发我们对于每个值单独计算。
题目中真正有意义的合并只有\(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);
}
}