bzoj 4998 星球联盟 Posted on 2019-05-13 | Views: 离线,并查集维护点双. 一个比较直接的做法是将点双缩点,然后用 $LCT$ 维护缩点后的树. 其实也可以直接用并查集做.因为只有加边的操作,所以可以离线处理出最后图的一颗生成树. 然后加边时,若为树边,答案显然是 $No$ .否则用并查集将那两个点合并起来,同时维护 $siz$ 就好了. 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495#include<bits/stdc++.h>using namespace std;typedef long long ll;inline int read(){ int out=0,fh=1; char jp=getchar(); while ((jp>'9'||jp<'0')&&jp!='-') jp=getchar(); if (jp=='-') fh=-1,jp=getchar(); while (jp>='0'&&jp<='9') out=out*10+jp-'0',jp=getchar(); return out*fh;}const int MAXN=2e5+10;int n,m,q;int ecnt=0,head[MAXN],to[MAXN<<2],nx[MAXN<<2];void addedge(int u,int v){ ++ecnt; to[ecnt]=v; nx[ecnt]=head[u]; head[u]=ecnt;}int f[MAXN],siz[MAXN];int Find(int x){ return x==f[x]?x:(f[x]=Find(f[x]));}int U[MAXN<<1],V[MAXN<<1];int fa[MAXN],dep[MAXN];void dfs(int u,int Fa){ fa[u]=Fa; dep[u]=dep[Fa]+1; for(int i=head[u];i;i=nx[i]) { int v=to[i]; if(v!=Fa) dfs(v,u); }}int treeedge[MAXN<<1];void merge(int x,int y){ x=Find(x),y=Find(y); while(x!=y) { if(dep[x]<dep[y]) swap(x,y); int fx=Find(fa[x]); f[x]=fx; siz[fx]+=siz[x]; x=fx; }}int main(){ n=read(),m=read(),q=read(); for(int i=1;i<=n;++i) f[i]=i; for(int i=1;i<=m+q;++i) { int u=read(),v=read(); U[i]=u,V[i]=v; int x=Find(u),y=Find(v); if(x!=y) { f[x]=y; addedge(u,v); addedge(v,u); treeedge[i]=1; } } for(int i=1;i<=n;++i) if(!fa[i]) dfs(i,i); for(int i=1;i<=n;++i) f[i]=i,siz[i]=1; for(int i=1;i<=m;++i) if(!treeedge[i]) merge(U[i],V[i]); for(int i=m+1;i<=m+q;++i) { if(treeedge[i]) puts("No"); else { merge(U[i],V[i]); printf("%d\n",siz[Find(U[i])]); } } return 0;}