题目分析:
本题初步浏览题目就知道是并查集的模板题,数据输入范围N为1~1000,则M的范围为0~1000^2,通过结构体记录每一对连线的关系,p[]数组记录每个节点的跟,对于k次查询,每次都要重新维护p[]数组,而每次的区别在于都要排除被占领的节点重新维护p[]数组的节点的链接关系,而最终的答案就是集合数-2(占领点一定是单独的集合,n个集合需要n-1条边就能相连)
1 #include<iostream> 2 using namespace std; 3 4 struct Node{ 5 int from; 6 int to; 7 }a[1000005]; 8 int p[1005]; 9 int n, m, k; 10 11 int find(int x){ 12 int y = x; 13 while(p[x] != x){ 14 x = p[x]; 15 } 16 //路径压缩 此时x是根 17 while(p[y] != x){ 18 int t = y; //对于路径上的每个y节点都要保留一下 19 y = p[y]; //此时我们还是按照上述的顺序去查询根 20 p[t] = x; //y已经变的p[y]的值,而t则记录了之前y的值 而p[之前的y]已经用不到了,我们将其路径压缩,把它的跟直接变为x 21 } 22 return x; 23 } 24 25 void Union(int x, int y){ 26 int fx = find(x); 27 int fy = find(y); 28 if(fx != fy){ 29 p[fx] = fy; 30 } 31 } 32 33 void init(int occupy){ 34 for(int i = 1; i <= n; i++) p[i] = i; 35 for(int i = 1; i <= m; i++){ 36 //根据a中的一对一对的关系维护并查集 同时注意被占领的城市不参与其中 37 if(a[i].from != occupy && a[i].to != occupy){ 38 Union(a[i].from, a[i].to); 39 } 40 } 41 } 42 43 void run(){ 44 int ans = 0; 45 for(int i = 1; i <= n; i++){ 46 if(p[i] == i){ 47 ans++; 48 } 49 } 50 printf("%d ", ans - 2); 51 } 52 53 int main(){ 54 while(scanf("%d%d%d", &n, &m, &k) != EOF){ 55 for(int i = 1; i <= m; i++){ 56 scanf("%d%d", &a[i].from, &a[i].to); 57 } 58 int occupy; 59 for(int i = 1; i <= k; i++){ 60 scanf("%d", &occupy); 61 //每次都要初始化并查集 62 init(occupy); 63 //获取每次至少要添加的线条数 64 run(); 65 } 66 } 67 return 0; 68 }