传送门:BZOJ1015
离线这道题就好做了。
使用并查集维护。
我们将所有未摧毁的点对间关系维护后,反向操作。即先加入最后一个被摧毁的点,统计答案,再加入倒数第二个被摧毁的点,统计答案……
不过代码有些微妙。
代码上的小细节见下。
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int l[400005],r[400005];
int des[400005];
int n,m;
int tot;
vector<int> G[800005];
int fa[800005];
int num[400005];
bool used[800005];
int ans;
void AddEdge(int from,int to)
{
G[from].push_back(to);
G[to].push_back(from);
}
int find(int a)
{
if(fa[a]!=a)
fa[a]=find(fa[a]);
return fa[a];
}
bool Near(int a,int b)
{
return find(a)==find(b);
}
void Union(int a,int b)
{
fa[find(a)]=find(b);
}
void Readdata()
{
// freopen("loli.in","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d",&l[i],&r[i]);
scanf("%d",&tot);
for(int i=1;i<=tot;i++)
scanf("%d",&des[i]);
}
void First()
{
for(int i=0;i<=n;i++)
fa[i]=i;
memset(used,false,sizeof(used));
for(int i=1;i<=tot;i++)
used[des[i]]=true;
for(int i=1;i<=m;i++)
AddEdge(l[i],r[i]);
for(int i=0;i<n;i++){
if(used[i])
continue;
for(int j=0;j<G[i].size();j++){
int u=G[i][j];
if(!used[u])
Union(i,u);
}
}
for(int i=0;i<n;i++)
if(!used[i]&&fa[i]==i)
ans++;
}
void Solve()
{
for(int i=tot;i>0;i--){
num[i]=ans;
int u=des[i];
used[u]=false;
ans++;
for(int j=0;j<G[u].size();j++){
int v=G[u][j];
if(!used[v])
if(!Near(v,u)){
Union(v,u);
ans--;
//printf("%d %d\n",u,v);
}
}
}
num[0]=ans;
for(int i=0;i<=tot;i++)
printf("%d\n",num[i]);
}
void Close()
{
fclose(stdin);
fclose(stdout);
}
int main()
{
Readdata();
First();
Solve();
Close();
return 0;
}