BZOJ 1015: [JSOI2008]星球大战starwar(并查集求连通块+离线处理)

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

题意:

 

思路:
好题啊!!!

这道题目需要离线处理,先把所有要删的点给保存下来,然后逆序加点,这样就把原来的删点变为了加点,加点的话计算连通块就方便的多,具体参见代码。

  1 #include<iostream>
  2 #include<algorithm>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<sstream>
  6 #include<vector>
  7 #include<stack>
  8 #include<queue>
  9 #include<cmath>
 10 #include<map>
 11 #include<set>
 12 using namespace std;
 13 typedef long long ll;
 14 typedef pair<int,ll> pll;
 15 const int INF = 0x3f3f3f3f;
 16 const int maxn=200000+5;
 17 
 18 int n, m;
 19 int k;
 20 int tot;
 21 int cnt;
 22 int q[2*maxn];
 23 int p[2*maxn];
 24 int vis[2*maxn];
 25 int del[2*maxn];
 26 int ans[2*maxn];
 27 int head[2*maxn];
 28 
 29 struct node
 30 {
 31     int v,next;
 32 }e[2*maxn];
 33 
 34 void addEdge(int u,int v)
 35 {
 36     e[tot].v=v;
 37     e[tot].next=head[u];
 38     head[u]=tot++;
 39 }
 40 
 41 int Find(int x)
 42 {
 43     return x==p[x]?x:p[x]=Find(p[x]);
 44 }
 45 
 46 void update(int u)
 47 {
 48     for(int i=head[u];i!=-1;i=e[i].next)
 49     {
 50         int v=e[i].v;
 51         if(vis[v])  //如果v之前已经访问过了并且u和v不在同一连通块中,那么合并并且减少一个连通块
 52         {
 53             int x=Find(u);
 54             int y=Find(v);
 55             if(x!=y)  {p[x]=y;cnt--;}
 56         }
 57     }
 58 }
 59 
 60 int main()
 61 {
 62     //freopen("in.txt","r",stdin);
 63     while(~scanf("%d%d",&n,&m))
 64     {
 65         tot=0;
 66         memset(head,-1,sizeof(head));
 67         memset(del,0,sizeof(del));
 68         memset(vis,0,sizeof(vis));
 69         for(int i=0;i<n;i++)  p[i]=i;
 70         while(m--)
 71         {
 72             int u,v;
 73             scanf("%d%d",&u,&v);
 74             addEdge(u,v);
 75             addEdge(v,u);
 76         }
 77 
 78         scanf("%d",&k);
 79         for(int i=1;i<=k;i++)  {scanf("%d",&q[i]);del[q[i]]=1;}
 80 
 81         cnt=0;
 82         for(int i=0;i<n;i++)
 83         {
 84             if(!del[i])
 85             {
 86                 cnt++; //先把它单独作为一个连通块
 87                 update(i);
 88                 vis[i]=1;
 89             }
 90         }
 91 
 92         ans[k+1]=cnt;
 93         for(int i=k;i>=1;i--)  //加点
 94         {
 95             cnt++;
 96             update(q[i]);
 97             vis[q[i]]=1;
 98             ans[i]=cnt;
 99         }
100         for(int i=1;i<=k+1;i++)  printf("%d\n",ans[i]);
101     }
102     return 0;
103 }
posted @ 2017-08-10 10:03  、苏州城外的微笑  阅读(...)  评论(...编辑  收藏