题目:https://www.luogu.org/problemnew/show/1197
此题不能按时间顺序进行删点、求连通块数量,而应打破时间的思维,先形成一张没有要删去的点的图,再从后往前逐个加点,存储连通块数量;
这样将删点问题转化为倒序的加点问题,通过并查集快速求出新图中连通块数量。
代码如下:
#include<iostream> #include<cstdio> using namespace std; int n,m,k,a,b,ct,cnt[400005],pla[400005],fa[400005],ans[400005],s; bool c[400005]; struct N{ int to;int next; }edge[400005]; int find(int x) { if(fa[x]==x)return x; else return fa[x]=find(fa[x]); } void add(int x,int y) { ct++; edge[ct].to=y; edge[ct].next=cnt[x]; cnt[x]=ct; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d",&a,&b); add(a,b);add(b,a); } scanf("%d",&k); for(int i=1;i<=k;i++) scanf("%d",&pla[i]),c[pla[i]]=1; s=n-k; for(int i=0;i<n;i++)fa[i]=i; for(int i=0;i<n;i++) { if(c[i])continue; for(int j=cnt[i];j;j=edge[j].next) { if(c[edge[j].to])continue; int v=find(edge[j].to); int u=find(i); if(u!=v)fa[u]=v,s--; } } for(int i=k;i>=1;i--) { ans[i]=s;c[pla[i]]=0;s++; for(int j=cnt[pla[i]];j;j=edge[j].next) { if(c[edge[j].to])continue; int v=find(edge[j].to); int u=find(pla[i]); if(u!=v)fa[u]=v,s--; } } printf("%d ",s); for(int i=1;i<=k;i++) printf("%d ",ans[i]); return 0; }