老旧的桥题解

题目:

现在有 N N N个岛屿和M坐桥。

i i i个桥双向连接着第 A A Ai和第 B B Bi个岛屿。

刚开始的时候,我们可以用其中的一些桥在任何两个岛之间旅行。

然而,经调查显示,这些桥都将会因老旧而倒塌,而且倒塌的顺序为从第1座桥到第M坐桥。

由于剩下的桥不能够使第 a a a个岛和第 b b b个岛互相到达,这会使岛屿对变得很不方便。

对于每一个,找出在第i ( 1 < = i < = M ) (1 <= i <= M) (1<=i<=M)座桥倒塌之后,这样不方便的岛屿对 ( a , b ) ( a < b ) (a, b)(a < b) (a,b)(a<b)有多少?

输入格式:

第一行有两个正整数和,分别表示岛屿的 N N N数量和桥的 M M M数量。

接下来有 M M M行,每一行有两个整数 A A Ai和 B B Bi,分别表示第i座桥连接的两个岛屿的编号。

输出格式:

输出有 M M M行,每一行输出一个整数,表示第座桥倒塌之后不方便到达的岛屿对 ( a , b ) ( a < b ) (a, b)(a < b) (a,b)(a<b)的数量,结果可能会超出 32 32 32位的 i n t int int大小。

输入:

输入样例1:

4 5
1 2
3 4
1 3
2 3
1 4

输入样例2:

6 5
2 3
1 2
5 6
3 4
4 5

输出:

输出样例1:

0
0
4
5
6

输出样例2:

8
9
12
14
15

题解:

首先毁桥的方法比较困难,我们可以把从1——m依次毁桥,想成从m——1依次建桥,用一个数组tot[i]来存建到第i座桥的联通数,初始化tot[m + 1]为n * (n - 1) / 2即不建任何桥,点与点的联通数。那么tot[i]该如何转移呢?因为tot[i + 1]表示建到第i + 1座桥的联通数,所以tot[i] = tot[i + 1] - 建第i座桥新增的联通数。

局部代码:

 tot[m + 1] = n * (n - 1) / 2;//初始化
    for (int i = m; i >= 2; i--) {//从m座桥一直建到第二座桥
    	int e = FindSet(c[i].a), f = FindSet(c[i].b);
    	if(e != f){//如果连了第i座桥可以新增联通点数
			UnionSet(c[i].a, c[i].b);
    		int q = sum[e] * sum[f];//sum[i]用来存第i个点可到达的点
    		//q表示新增的联通数
       		tot[i] = tot[i + 1] - q;//tot[i] = tot[i + 1] - 建第i座桥新增的联通数。
        	sum[FindSet(c[i].a)] = sum[e] + sum[f];//因为联通了第i座桥,e和f两点可互相连通,则e,f分别可到达的点也可以共享了
		}
        else{//如果不可以即是建第i+1座桥的联通数
        	tot[i] = tot[i + 1];
		}
    }

代码:

#include 
#include 
using namespace std;
long long fa[300005], rank[300005];
long long tot[300005], sum[300005];
struct node {
   long long a, b;
} c[300005];
void MakeSet(int n) {
    for (int i = 1; i <= n; i++) {
        fa[i] = i;
        rank[i] = 0;
        sum[i] = 1;
    }
}
int FindSet(int x) {
    if (fa[x] != x) {
        fa[x] = FindSet(fa[x]);
    }
    return fa[x];
}
void UnionSet(int w, int e) {
    int u = FindSet(w), v = FindSet(e);
    if (u == v) {
        return;
    }
    if (rank[u] > rank[v]) {
        fa[v] = u;
    } else {
        fa[u] = v;
        if (rank[u] == rank[v]) {
            rank[u]++;
        }
    }
}
int main() {
    long long n, m, q, x, y;
    scanf("%lld %lld", &n, &m);
    MakeSet(n);
    for (int i = 1; i <= m; i++) {
        scanf("%lld %lld", &c[i].a, &c[i].b);
    }
    tot[m + 1] = n * (n - 1) / 2;
    for (int i = m; i >= 2; i--) {
    	int e = FindSet(c[i].a), f = FindSet(c[i].b);
    	if(e != f){
			UnionSet(c[i].a, c[i].b);
    		int q = sum[e] * sum[f];
       		tot[i] = tot[i + 1] - q;
        	sum[FindSet(c[i].a)] = sum[e] + sum[f];
		}
        else{
        	tot[i] = tot[i + 1];
		}
    }
    for (int i = 2; i <= m + 1; i++) {
        printf("%lld\n", tot[i]);
    }
    return 0;
}

你可能感兴趣的:(并查集,算法)