【follow】BZOJ1015: [JSOI2008]星球大战starwar

来源:互联网 发布:成塔软件 编辑:程序博客网 时间:2024/05/22 00:42

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1015

意思是给一个图,每次删除一个点和与其相关的连边,每次求出连通块的个数。

这道题按照题目的说法,很不好想。但是如果把题目倒过来,我们只需要每次在图中添加节点,并判断添加的节点是否:{产生新的连通块,将两个已有连通块连在一起}即可。

这就是并查集的思想。

首先我们建一张参照图,建立题中的所有边。

用ans表示现在的连通块个数,初始为0即可。

然后统计出那些点没有被踢出(用ove来表示),建立一张新的图。统计这个图的联通块数量,存为ans,存入最终输出的答案数组中。

逆序添加点,每次添加,更新ans,存入最后输出的答案数组中。

最后逆序输出答案数组即可。

[更新操作]insert

首先,此点产生了一个新的连通块,ans++.    (*)

我们加入了一个点。首先要按照原图,寻找相连的边,如果此边通向的点在图中,并且与加入的点不在一个连通块(用并查集即可),那么ans--,并连接这两个连通块。

不在一个连通块的话就不用管了。(因为已经ans++了。(*处))

#include<iostream>#include<cmath>#include<cstring>#include<cstdio>#include<algorithm>using namespace std;const int maxn=400010;int fa[maxn],head[maxn],cnt,d[maxn],gnt;bool ove[maxn],use[maxn];int n,m,k,sum[maxn],ans;struct edge{  int nxt,to;}e[maxn],g[maxn];void add1(int x,int y)//对照组(所有边) {  e[++cnt].to=y;  e[cnt].nxt=head[x];  head[x]=cnt;}void add2(int x,int y)//正式组(被攻打之后) {  g[++gnt].to=y;  g[gnt].nxt=head[x];  head[x]=gnt;}int find(int x){  if(fa[x]==x)return x;  return fa[x]=find(fa[x]);}void insert(int x){  for(int i=head[x];i;i=e[i].nxt)   if(use[e[i].to]==1)//在图中     {      add2(x,e[i].to);  if(find(e[i].to)!=find(x))   {     ans--;     fa[find(x)]=find(e[i].to);   }    }}int main(){  cin>>n>>m;  for(int i=1;i<=n;i++)fa[i]=i;  int x,y;  for(int i=1;i<=m;i++)   {    scanf("%d%d",&x,&y);    x++,y++;    add1(x,y);    add1(y,x);   }   cin>>k;  for(int i=1;i<=k;i++)   {     scanf("%d",&d[i]);     d[i]++;     ove[d[i]]=1;   }  for(int i=1;i<=n;i++)  if(ove[i]==0)   {     insert(i);     ans++;     use[i]=1;   }   sum[k+1]=ans;   for(int i=k;i>=1;i--)   {    insert(d[i]);    ans++;    use[d[i]]=1;    sum[i]=ans;   }  for(int i=1;i<=k+1;i++)printf("%d\n",sum[i]); return 0;}

0 0
原创粉丝点击