传送门1
传送门2
写在前面:无
思路:比较简单的并查集题目,正着做很难,我们可以倒着想,询问倒着排,相当于每次加入一个星球并把可以连的边都连上,查一下联通块数量就行了
注意:必须两个顶点都没有被摧毁才能进行并查集操作
代码:
#include"bits/stdc++.h"
using namespace std;
int tot=1,x,y,n,m,k,ans;
struct os
{
int u,v,next;
}a[400010];
int father[400010],first[400010],q[400010],outs[400010];
bool flag[400010];
int in()
{
int t=0;
char ch=getchar();
while (ch>'9'||ch<'0') ch=getchar();
while (ch>='0'&&ch<='9') t=t*10+ch-'0',ch=getchar();
return t;
}
void add(int x,int y)
{
a[++tot].u=x;
a[tot].v=y;
a[tot].next=first[x];
first[x]=tot;
}
int find(int x)
{
if (x!=father[x]) father[x]=find(father[x]);
return father[x];
}
void unions(int x,int y)
{
if (x!=y) father[x]=y,ans--;
}
main()
{
n=in();m=in();
for (int i=1;i<=m;i++)
x=in(),y=in(),
add(x,y),add(y,x);
k=in();
ans=n;
for (int i=1;i<=k;i++)
{
q[i]=in();
ans--;
flag[q[i]]=1;
}
for (int i=1;i<=n;i++) father[i]=i;
for (int i=1;i<=n;i++)
if (!flag[i])
for (int j=first[i];j;j=a[j].next)
if (!flag[a[j].v])
unions(find(a[j].v),find(a[j].u));
for (int i=k;i>=1;i--)
{
outs[i]=ans++;
flag[q[i]]=0;
for (int j=first[q[i]];j;j=a[j].next)
if (!flag[a[j].v])
unions(find(a[j].v),find(a[j].u));
}
printf("%d\n",ans);
for (int i=1;i<=k;i++)
printf("%d\n",outs[i]);
}