题目链接:https://www.lanqiao.cn/problems/19736/learning/
个人评价:难度 2 星(满星:5)
前置知识:并查集
整体思路
- 按边权从小到大处理,将处理过的边的两个端点合入一个并查集中;
- 由此,在处理到第 i i i 条边时,如果边的两个端点不在并查集中,那么这两个端点各自在的并查集中所有端点,都必须经过这条边才能到达对面的端点,所以该边两端所有点之间的“最贵”收费就是这条边的权值,因此如果该边权值在区间 [ L , R ] [L, R] [L,R] 内,那么这条边对答案的贡献就是两边并查集节点数的乘积。
过题代码
#include <bits/stdc++.h>
using namespace std;typedef long long LL;
const int maxn = 200000 + 100;
struct Edge {int u, v, w;Edge() {}Edge(int u, int v, int w): u(u), v(v), w(w) {}
};bool operator<(const Edge &a, const Edge &b) {return a.w < b.w;
}int n, m, l, r, u, v, w;
LL ans;
int fa[maxn], num[maxn];
Edge edge[maxn];void init() {for (int i = 1; i <= n; ++i) {fa[i] = i;num[i] = 1;}
}int findF(int x) {return x == fa[x] ? x : fa[x] = findF(fa[x]);
}void union_(int x, int y) {x = findF(x);y = findF(y);if (x != y) {fa[x] = y;num[y] += num[x];}
}int main() {
#ifdef ExRocfreopen("test.txt", "r", stdin);
#endif // ExRocios::sync_with_stdio(false);cin >> n >> m >> l >> r;init();for (int i = 1; i <= m; ++i) {cin >> edge[i].u >> edge[i].v >> edge[i].w;}sort(edge + 1, edge + 1 + m);for (int i = 1; i <= m; ++i) {u = findF(edge[i].u);v = findF(edge[i].v);w = edge[i].w;if (w >= l && w <= r && u != v) {ans += num[u] * num[v];}union_(u, v);}cout << ans << endl;return 0;
}