BZOJ 1015 [JSOI2008]星球大战starwar (逆序并查集)

BZOJ 1015

思路:并查集只有联边的作用,无法做到拆边,因此采取逆序做法。先将边拆掉,再用并查集进行联边,不同联通块相连则联通块数目减一。

 1 #include<bits/stdc++.h>
 2 #define se second
 3 #define fi first
 4 #define ll long long
 5 #define ls l,m,rt<<1
 6 #define rs m+1,r,rt<<1|1
 7 #define Pii pair<int,int>
 8 #define pb push_back
 9 #define fio ios::sync_with_stdio(false);cin.tie(0)
10 using namespace std;
11 const double pi=acos(-1.0);
12 const int N=1e6+5;
13 using namespace std;
14 int fa[N];
15 int tot;
16 struct node{
17     int u,v;
18 }q[200010];
19 int a[N];
20 bool vis[N];
21 int ans[N];
22 vector<int>un[N];
23 void init (int x){
24     for(int i=0;i<=x;i++){
25         fa[i]=i;
26     }
27 }
28 int findfa(int x){
29     return fa[x]==x?x:fa[x]=findfa(fa[x]);
30 }
31 void unite(int x,int y){
32     x=findfa(x);
33     y=findfa(y);
34     if(x!=y){
35         fa[y]=x;
36         tot--;//不同联通块相连则联通块数目减一
37     }
38 }
39 
40 int main(){
41     int n,m;
42     scanf("%d %d",&n,&m);
43     init(n);
44     for(int i=0;i<m;i++){
45         scanf("%d %d",&q[i].u,&q[i].v);
46     }
47     int k;
48     scanf("%d",&k);
49     for(int i=1;i<=k;i++){
50         scanf("%d",&a[i]);
51         vis[a[i]]=1;
52     }
53     tot=n-k;
54     for(int i=0;i<m;i++){
55         if(vis[q[i].u]||vis[q[i].v]){// 如果一边是被拆掉的,则将两个点互相对方的不可行数组中(双向存边),否则直接联边
56             un[q[i].u].pb(q[i].v);
57             un[q[i].v].pb(q[i].u);
58         }
59         else {
60             unite(q[i].u,q[i].v);
61         }
62     }
63     ans[k+1]=tot;
64     for(int i=k;i>0;i--){
65         tot++;//将该点设为未被打击点,并将联通块加一,因为多出来一个未被打击的联通块
66         int m=a[i];
67         vis[m]=0;
68         for(int j=0;j<un[m].size();j++){
69             if(!vis[un[m][j]])
70                 unite(m,un[m][j]);
71         }
72         ans[i]=tot;
73     }
74     for(int i=1;i<=k+1;i++)printf("%d\n",ans[i]);
75     return 0;
76 }

 

posted @ 2018-02-01 21:45  采蘑菇的小西佬  阅读(...)  评论(...编辑  收藏