洛谷 P1197 [JSOI2008]星球大战 解题思路: AC代码:

题目传送门

并查集,逆序做(即先假设给的k个星球全都被炸,求出此时的联通块个数,就是经过k次打击的联通块个数。然后再加上最后一个被炸的星球,就求出了经过k-1次打击的联通块个数。。。以此类推,最后把所有点都加进去,就求出了经过0次打击后连同块个数)

//转载自:https://www.luogu.org/blog/user25199/solution-p1197

AC代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<bits/stdc++.h>
 4 
 5 using namespace std;
 6 
 7 int n,m,fa[400001],tot,br[400001],sum[400002],head[400001],k;
 8 struct kkk {
 9     int from,next,to;
10 }g[400001];
11 bool vis[400001];
12 
13 void add(int a,int b) {
14     g[++tot].from = a;
15     g[tot].to = b;
16     g[tot].next = head[a];
17     head[a] = tot; 
18 }
19 
20 int find_father(int x) {
21     if(fa[x] == x) return x;
22     return fa[x] = find_father(fa[x]);
23 }
24 
25 void merge(int x,int y) {
26     int x1 = find_father(x);
27     int y1 = find_father(y);
28     fa[y1] = x1;
29 }
30 
31 int main() {
32     scanf("%d%d",&n,&m);
33     for(int i = 0;i <= n; i++) {
34         fa[i] = i;
35         head[i] = -1;
36     }
37     for(int i = 1;i <= m; i++) {
38         int x,y;
39         scanf("%d%d",&x,&y);
40         add(x,y);
41         add(y,x);
42     }
43     scanf("%d",&k);
44     for(int i = 1;i <= k; i++) {
45         scanf("%d",&br[i]);
46         vis[br[i]] = 1;
47     }
48     int ans = n - k;
49     for(int i = 1;i <= 2 * m; i++) {
50         if(!vis[g[i].from] && !vis[g[i].to] && find_father(g[i].from) != find_father(g[i].to)){
51             ans--;
52             merge(g[i].from,g[i].to);
53         }        
54     }
55     sum[k+1] = ans;
56     for(int i = k;i >= 1; i--) {
57         ans++;
58         vis[br[i]] = 0;
59         for(int j = head[br[i]];j != -1;j = g[j].next) {
60             if(!vis[g[j].to] && find_father(br[i]) != find_father(g[j].to)) {
61                 ans--;
62                 merge(br[i],g[j].to);
63             }
64         }
65         sum[i] = ans;
66     }
67     for(int i = 1;i <= k + 1; i++)
68         printf("%d
",sum[i]);
69     return 0;
70 }