题目:BZOJ1015、洛谷P1197。
题目大意:有n个星球,星球之间有隧道连接。
现在依次有一些星球被摧毁,摧毁后就算不存在了,与它连接的隧道也没了。
如果某些星球互相可以到达,我们就说它们在一个连通块内。现在首先输出原来的连通块个数,然后依次输出每次摧毁后这些星球形成的连通块个数。
如果只有一个点,也算一个连通块。
解题思路:如果每次是连接一些星球的话,那我们可以用并查集。但此题是摧毁星球,也就是删除边,怎么办?
容易想到,离线处理询问,先算出最终的状态,然后往上倒着推回来,就是加边的操作了,就可使用并查集。
其他没什么技巧,时间复杂度$O(n+m+k)$。
C++ Code:
#include<cstdio>
#include<cstring>
int n,m,k,bom[400005],head[400005],fa[400005],all[400005];
bool vis[400005];
struct edge{
int to,nxt;
}e[400005];
int dad(int x){return x==fa[x]?x:(fa[x]=dad(fa[x]));}
int main(){
//freopen("input","r",stdin);
int cnt=0;
memset(head,0,sizeof head);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i){
int u,v;
scanf("%d%d",&u,&v);
e[++cnt]=(edge){v,head[u]};
head[u]=cnt;
e[++cnt]=(edge){u,head[v]};
head[v]=cnt;
}
int ans=n;
memset(vis,1,sizeof vis);
scanf("%d",&k);
for(int i=1;i<=k;++i)scanf("%d",&bom[i]),vis[bom[i]]=false,--ans;
for(int i=1;i<=n;++i)fa[i]=i;
for(int i=1;i<=n;++i)
if(vis[i]){
int a=dad(i);
for(int j=head[i];j;j=e[j].nxt)
if(vis[e[j].to]){
int b=dad(e[j].to);
if(a!=b){
fa[b]=a;
--ans;
}
}
}
all[k]=ans;
for(int i=k;i;--i){
vis[bom[i]]=true;
++ans;
int a=dad(bom[i]);
for(int j=head[bom[i]];j;j=e[j].nxt)
if(vis[e[j].to]){
int b=dad(e[j].to);
if(a!=b){
fa[b]=a;
--ans;
}
}
all[i-1]=ans;
}
for(int i=0;i<=k;++i)printf("%d\n",all[i]);
return 0;
}