去题面的传送门
记得之前做过一个跟这个题很类似的。那道题是每次删一条边,问联通块的个数。正解就是离线处理,然后倒序加边,用并查集维护。而这道题是删点,同样想到离线处理然后倒序加点,唯一的不同是这道题要把这个点连接的所有边都for一遍。。。
话不多说,上代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=200000+100;
int cnt,n,m,k;
int fist[maxn<<1],nxt[maxn<<1],num[maxn<<1],ans[maxn<<1],fa[maxn<<1];
bool vis[maxn<<1];
struct hh
{
int f,t;
}e[maxn<<1];
void build(int f,int t)
{
e[++cnt]=(hh){f,t};
nxt[cnt]=fist[f];
fist[f]=cnt;
}
int find(int x)
{
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
int main()
{
memset(fist,-1,sizeof(fist));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i)
{
int x,y;
scanf("%d%d",&x,&y);
build(x,y);
build(y,x);
}
scanf("%d",&k);
for(int i=1;i<=k;++i)
{
scanf("%d",&num[i]);
vis[num[i]]=true;
}
for(int i=1;i<=n;++i) fa[i]=i;
ans[k+1]=n-k;
for(int i=1;i<=cnt;i+=2)
{
int u=e[i].f,v=e[i].t;
int f1=find(u),f2=find(v);
if(!vis[u]&&!vis[v]&&f1!=f2)
{
f1=fa[f2];
ans[k+1]--;
}
}
for(int i=k;i>=1;--i)
{
ans[i]=ans[i+1]+1;
int u=num[i];
vis[u]=false;
for(int j=fist[u];j!=-1;j=nxt[j])
{
int v=e[j].t;
if(vis[v]) continue;
int f1=find(u),f2=find(v);
if(f1!=f2)
{
ans[i]--;
fa[f1]=f2;
}
}
}
for(int i=1;i<=k+1;++i) printf("%d\n",ans[i]);
return 0;
}