显然这是一道并查集判联通块的题目,并查集的作用是合并和查询,题目中要求输出炸毁某个点后连通块的个数,但并查集貌似不支持拆分操作 ,可能是我不会,所以我们可以期初先把所有点先摧毁,看看有多少联通块,然后倒着来做,从摧毁的最后一个点开始修建,看看每修建一个点,能减少多少个联通块,每修建一个点记录一下答案,最后正序输出即可。
AC代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define maxn 200005
using namespace std;
struct node
{
int st,ed,nxt;
};
node edge[maxn<<1];
int n,m,k,ans[maxn<<1],h[maxn<<1],f[maxn<<1],first[maxn<<1],cnt;
//int st[maxn],ed[maxn];
bool bum[maxn<<1];
inline void add_edge(int s,int e)
{
cnt++;
edge[cnt].st=s;
edge[cnt].ed=e;
edge[cnt].nxt=first[s];
first[s]=cnt;
return;
}
inline int find(int b)
{
if(f[b]==b) return f[b];
return f[b]=find(f[b]);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int s,e;
scanf("%d%d",&s,&e);
add_edge(s+1,e+1); add_edge(e+1,s+1);
//st[i]=s+1; ed[i]=e+1;
}
scanf("%d",&k);
int num=n;
for(int i=1;i<=k;i++)
{
scanf("%d",&h[i]);
h[i]++;
bum[h[i]]=true;
num--;
}
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=2*m;i++)
{
if(bum[edge[i].st]==false&&bum[edge[i].ed]==false)
{
int f1=find(edge[i].st),f2=find(edge[i].ed);
if(f1!=f2)
{
num--;
f[f1]=f2;
}
}
}
ans[k+1]=num;
for(int i=k;i>=1;i--)
{
bum[h[i]]=false;
num++;
for(int j=first[h[i]];j!=0;j=edge[j].nxt)
{
int e=edge[j].ed;
if(!bum[e])
{
int f1=find(h[i]);
int f2=find(e);
if(f1!=f2)
{
f[f1]=f2;
num--;
}
}
}
ans[i]=num;
}
for(int i=1;i<=k+1;i++)
printf("%d\n",ans[i]);
return 0;
}