并查集,动态连通性
http://acm.csu.edu.cn/OnlineJudge/problem.php?cid=2079&pid=1
n,m代码n个点,m条边
随之而来m条边
q 代表q个操作
q行,每行一个序号,代表将第m个输入的边删除,问删除后有多少个连通块。
思路:我们将m条边,和q询问记录下来,并将要删除的边标记。
然后对那些不用的边使用并查集,并算出有多少个连通块。
然后从最后一个询问开始,依次将那些边加入并查集,如果加入时,两个点的父亲不同,那么连通块就减少一个
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 #include <algorithm> 5 #include <iostream> 6 #include <queue> 7 #include <stack> 8 #include <vector> 9 #include <map> 10 #include <set> 11 #include <string> 12 #include <math.h> 13 using namespace std; 14 #pragma warning(disable:4996) 15 typedef long long LL; 16 const int INF = 1 << 30; 17 const int N = 100000 + 10; 18 int u[N], v[N], q[N]; 19 int father[N]; 20 int ans[N]; 21 bool vis[N]; 22 void init(int n) 23 { 24 for (int i = 0; i <= n; ++i) 25 father[i] = i; 26 memset(vis, 0, sizeof(vis)); 27 } 28 int find(int x) 29 { 30 if (x == father[x]) 31 return x; 32 return father[x] = find(father[x]); 33 } 34 int main() 35 { 36 int n, m, i, Q; 37 while (scanf("%d%d", &n, &m) != EOF) 38 { 39 init(n); 40 for (i = 1; i <= m; ++i) 41 { 42 scanf("%d%d", &u[i], &v[i]); 43 } 44 scanf("%d", &Q); 45 for (i = 0; i < Q; ++i) 46 { 47 scanf("%d", &q[i]); 48 vis[q[i]] = true; 49 } 50 for (i = 1; i <= m; ++i) 51 { 52 if (!vis[i]) 53 { 54 int rx = find(u[i]); 55 int ry = find(v[i]); 56 if (rx != ry) 57 father[rx] = ry; 58 } 59 } 60 int tmp = 0; 61 for (i = 1; i <= n; ++i) 62 if (father[i] == i)//算出连通块 63 tmp++; 64 for (i = Q - 1; i >= 0; --i)//从后往前,加入那些要删除的边 65 { 66 int rx = find(u[q[i]]); 67 int ry = find(v[q[i]]); 68 ans[i] = tmp;// 69 if (rx != ry) 70 { 71 father[rx] = ry; 72 tmp--; 73 } 74 75 } 76 for (i = 0; i < Q; ++i) 77 i == Q - 1 ? printf("%d\n", ans[i]) : printf("%d ", ans[i]); 78 } 79 }