连通块个数很显然是集合个数,这题常规做法肯定是按照顺序连边,然后跑一边计算集合个数,很显然会超时,考虑逆序处理,即,把能摧毁的全部摧毁,统计连通块个数,代表最后一次操作后连通块个数,然后从最后一个开始补点,遍历其能连接的点,如果不在一个集合而且没被摧毁,那么连通块个数就要减1,即发现了应该联通但分属于两个集合的子集。
# include<iostream>
# include<stack>
using namespace std;
bool broken[200000+10];
typedef struct
{
int b,e;
}xinxi;
int fa[100000+10];
xinxi s[200000*2+10];
int f[200000*2+10];
int nex[200000*2+10];
int getf(int x)
{
if(x==fa[x])
return x;
else
{
fa[x]=getf(fa[x]);
return fa[x];
}
}
int len;
void add(int x,int y)
{
s[len].b=x;
s[len].e=y;
nex[len]=f[x];
f[x]=len;
len++;
}
int x[200000+10];
int y[200000+10];
int z[200000+10];
int ans1[200000+10];
int main( )
{
int n,m;
fill(f,f+200000*2+10,-1);
cin>>n>>m;
for(int i=1;i<=n;i++)
{
fa[i]=i;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x[i],&y[i]);
add(x[i],y[i]);
add(y[i],x[i]);
}
int t;
cin>>t;
int tt=t;
for(int i=1;i<=t;i++)
{
scanf("%d",&z[i]);
broken[z[i]]=1;
}
int ans=n-t;
for(int i=1;i<=m;i++)
{
if(!broken[x[i]]&&!broken[y[i]])
{
int t1=getf(x[i]);
int t2=getf(y[i]);
if(t1!=t2)
{
fa[t2]=t1;
ans--;
}
}
}
ans1[tt]=ans;
tt--;
for(int i=t;i>=1;i--)
{
broken[z[i]]=0;
ans++;
int x=f[z[i]];
while(x!=-1)
{
int j=s[x].e;
if(!broken[j])
{
int t1=getf(j);
int t2=getf(z[i]);
if(t1!=t2)
{
fa[t2]=t1;
ans--;
}
}
x=nex[x];
}
ans1[tt]=ans;
tt--;
}
for(int i=tt+1;i<=t;i++)
{
cout<<ans1[i]<<'\n';
}
return 0;
}