题意: 给定一张图,有删点和询问连通块个数两种操作
分析: 对于这个题目,如果要在删除点的同时维护图的形态,对于并查集来说是不可做的。因为并查集是不支持删除点的。那么,我们就可以将删点转化为加点,进行逆序处理,这样我们的思路就比较清晰了。
首先将没有删除的点连起来,再逆序将点一个一个放进去,每次维护放进去点以后的连通块,进行merge操作即可,若成功merge一次,则总连通块数量-1。
详细代码如下:
#include <bits/stdc++.h>
using namespace std;
int fa[400010],head[400010],num[400010],sum[400010],tot=0,ans;
bool del[400010],vis[400010];
struct edge{
int to,next;
}edge[400010];
int read() {
int ans=0,flag=1;
char ch=getchar();
while((ch>'9' || ch<'0') && ch!='-') ch=getchar();
if(ch=='-') flag=-1,ch=getchar();
while(ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar();
return ans*flag;
}
int find(int x) {
return x==fa[x]?x:fa[x]=find(fa[x]);
}
void addedge(int x,int y) {
edge[++tot].to=y;
edge[tot].next=head[x];
head[x]=tot;
}
void merge(int x,int y) {//合并操作
int fx=find(x),fy=find(y);
if(fx==fy) return ;
fa[fy]=fx;
ans--;
}
void work(int x) {
if(vis[x] || del[x]) return ;
vis[x]=true;
for(int i=head[x];i;i=edge[i].next) {
int to=edge[i].to;
if(del[to]) continue;
merge(x,to);
work(to);
}
}
int main() {
int n=read(),m=read();
for(int i=0;i<n;i++) {
fa[i]=i;
}
for(int i=1;i<=m;i++) {//这里是利用邻接表来存边
int x=read(),y=read();
addedge(x,y);
addedge(y,x);
}
int k=read();
ans=n-k;
for(int i=1;i<=k;i++) {
int x=read();
del[x]=true;
num[i]=x;
}
for(int i=0;i<n;i++) {//将没有删除的点先处理了
work(i);
}
sum[0]=ans;
for(int i=k;i>=1;i--) {//逆序将点加回
del[num[i]]=false;
ans++;
work(num[i]);
sum[i]=ans;
}
for(int i=1;i<=k;i++) {
printf("%d\n",sum[i]);
}
printf("%d\n",sum[0]);
return 0;
}
by:Chlience