并查集,逆序做(即先假设给的k个星球全都被炸,求出此时的联通块个数,就是经过k次打击的联通块个数。然后再加上最后一个被炸的星球,就求出了经过k-1次打击的联通块个数。。。以此类推,最后把所有点都加进去,就求出了经过0次打击后连同块个数)
//转载自:https://www.luogu.org/blog/user25199/solution-p1197
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\n",sum[i]); 69 return 0; 70 }
原文:https://www.cnblogs.com/lipeiyi520/p/11241188.html