题目:https://www.luogu.org/problemnew/show/1197
并查集。
难点是若依次去掉点在求连通块个数,时间太长。
精妙的思维:先全部读入,再逆向求连通块个数——增加点比删去点对于求个数更容易!
小技巧:求个数时可以先设个数s为n,每次合并一个fa[ ]就s - -;但此题中别忘了增加点时要s++,且一开始s不是n而是n-w。
#include<iostream> #include<cstdio> using namespace std; int n,m,x,y,xnt,nex[400005],fa[400005],w,de[400005],cnt,s,c[400005]; bool bb[400005]; struct Node{ int next,to; }edge[400005]; void add(int a,int b) { xnt++; edge[xnt].next=nex[a]; edge[xnt].to=b; nex[a]=xnt; } int find(int a) { if(fa[a]==a)return a; return fa[a]=find(fa[a]); } int main() { scanf("%d%d",&n,&m); for(int i=0;i<n;i++) fa[i]=i; for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); add(x,y);add(y,x); } scanf("%d",&w); s=n-w; for(int i=1;i<=w;i++) { scanf("%d",&x); de[++cnt]=x; bb[x]=1; } for(int i=0;i<n;i++) if(!bb[i]) { int u=find(i); for(int j=nex[i];j;j=edge[j].next) { int k=edge[j].to; if(bb[k])continue; int v=find(k); if(u!=v) { fa[v]=u; s--; } } } c[cnt+1]=s; for(int i=cnt;i;i--) { s++; int k=de[i]; bb[k]=0; int u=find(k); for(int j=nex[k];j;j=edge[j].next) { int r=edge[j].to; if(bb[r])continue; int v=find(r); if(u!=v) { fa[v]=u; s--; } } c[i]=s; } for(int i=1;i<=w+1;i++) printf("%d ",c[i]); return 0; }