去題面的傳送門
記得之前做過一個跟這個題很類似的。那道題是每次刪一條邊,問聯通塊的個數。正解就是離線處理,然後倒序加邊,用並查集維護。而這道題是刪點,同樣想到離線處理然後倒序加點,唯一的不同是這道題要把這個點連接的所有邊都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;
}