这道题是要求一个动态的联通块数量。
刚开始的想法是强连通,找割点,然后发现打不下去。
所以想到了并查集。可是并查集如何删点?
我这里采用了逆时间建边,就是先把最后结果的边建好,再把删的点加回去,维护联通块数量。
#include<cstdio>
#include<cstdlib>
#include<cstring>
bool v[500000];//是否被释放
int p[500000];
struct mod{int x,y,next;};
mod q[500000];
int f[500000];
int first[500000],len=0;
int ans[500000];
void ins(int x,int y)
{
len++;
q[len].x=x;
q[len].y=y;
q[len].next=first[x];
first[x]=len;
}
int find(int x)
{
if (f[x]==x)return x;
f[x]=find(f[x]);
return f[x];
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
memset(first,-1,sizeof(first));
for (int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
ins(x,y);
ins(y,x);
}
int k;
scanf("%d",&k);
memset(v,true,sizeof(v));
int num=n;
for (int i=1;i<=k;i++)
{scanf("%d",&p[i]);v[p[i]]=false;}
num-=k;
//printf("#%d\n",num);
for (int i=0;i<n;i++)f[i]=i;
for (int x=0;x<n;x++)
{
if (v[x]==false)continue;
for (int j=first[x];j!=-1;j=q[j].next)
{
int y=q[j].y;
if (v[y]==false)continue;
int fx=find(x),fy=find(y);
if (fx==fy)continue;
num--;
f[fx]=fy;
}
}
ans[k]=num;
//printf("*%d\n",num);system("pause");
for (int i=k;i>=1;i--)//倒着把点加入
{
int x=p[i];
v[x]=true;
for (int j=first[x];j!=-1;j=q[j].next)
{
int y=q[j].y;
if (v[y]==false)continue;
int fx=find(x),fy=find(y);
if (fx==x)
f[fx]=fy;
else
{
if (fx!=fy)
{
num--;
f[fx]=fy;
}
}
}
if (find(x)==x)num++;
ans[i-1]=num;
}
for (int i=0;i<=k;i++)
printf("%d\n",ans[i]);
}