题目: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 ",all[i]); return 0; }