有一类并查集题目中,起始是一张连了所有边的图。
给出一些操作,让一些边"断裂",再抛出一些问题。
这个时候,就可以考虑逆向合并边。
例题星球大战:https://www.luogu.com.cn/problem/P1197
思路:
因为每次轰炸星球,相当于把一个点完全抹除,相应的边也没了。
那么我们完全可以先读入要摧毁的点,标记起来,然后在剩余的边里面
挑出一些没有被摧毁的先建边。很神奇的,我们发现这个时候的连通块个数就是最后一次的个数
那我们再把最后一个被摧毁的点相应的边连起来,再看看连通块个数,这个答案就是倒数第二次的答案
不过要清楚一点。图中7和1起始也有边,但是我们从后逆推,那么1,3,5是已经被摧毁了的,所以我们暂时不能连边
还有一点,每次我们连完了一个点对应的边,就要把它的标记清楚。因为再往前逆推,那么这个点其实是没有被摧毁的
然后每次连接完一个点后,当前连通块个数都要+1
#include <bits/stdc++.h> using namespace std; const int maxn=400009; struct node{ int to,nxt; }d[maxn*2]; int head[maxn*2],cnt=1,pre[maxn],n,m,q; void add(int u,int v){ d[cnt].to=v,d[cnt].nxt=head[u],head[u]=cnt++; } int find(int x){ if(x!=pre[x]) pre[x]=find(pre[x]); return pre[x]; } void join(int q,int w){ pre[find(q)]=find(w); } int vis[maxn],a[maxn],ans[maxn],ww; void init() { for(int i=1;i<=n;i++) { if(vis[i]) continue;//被摧毁的星球不连边 for(int j=head[i];j;j=d[j].nxt) { if(vis[d[j].to]) continue; if(find(d[j].to)!=find(i)) ww--,join(d[j].to,i); } } } int main() { cin>>n>>m; for(int i=0;i<=n-1;i++) pre[i]=i; for(int i=1;i<=m;i++) { int l,r; cin>>l>>r; add(l,r);add(r,l); } cin>>q; ww=n-q; for(int i=1;i<=q;i++) { int x;cin>>x; vis[x]=1;a[i]=x; } init();//连初始边 for(int i=q;i>=1;i--) { ans[i]=ww; for(int k=head[a[i]];k;k=d[k].nxt) { int z=d[k].to; if(vis[z]) continue; if(find(z)!=find(a[i])) ww--,join(z,a[i]); } vis[a[i]]=0; ww++;//连通块个数加一 } cout<<ww<<endl; for(int i=1;i<=q;i++) cout<<ans[i]<<endl; }