题目链接:https://vjudge.net/contest/391866#problem/A
题目描述:
开始的想法是都减去最小的值,这样一个连通块就被分为了若干连通块,如此重复。但这样不好实现。
可以反过来去想。既然删点困难可以考虑其逆过程,加点。把所有点从大到小排序,然后放入集合中,然后遍历所有这个点相连的边(x,y),如果y在集合中,而且x,y还没有并查集合并,就合并它们,ans+=b[y]-b[x]
最后再把所有根的值都加到ans
代码:
const int maxn = 1e5+7; vector<int> to[maxn]; bool vis[maxn]; int fa[maxn]; int w[maxn]; ll ans; struct node { int x, y; }b[maxn]; bool cmp(node A, node B) { return A.x > B.x; } int found(int x) { return fa[x]==x ? x :fa[x] = found(fa[x]); } void unite(int x, int y) { y = found(y); fa[y] = x; ans += (w[y]-w[x]); } int main() { int t; cin >> t; while(t--) { ans = 0; int n, m; cin >> n >> m; for(int i = 1; i <= n; i++) { cin >> b[i].x; b[i].y = i; w[i] = b[i].x; fa[i] = i; to[i].clear(); } for(int i = 1; i <= m; i++) { int u, v; cin >> u >> v; to[u].push_back(v); to[v].push_back(u); } sort(b+1, b+n+1, cmp); memset(vis,0,sizeof(vis)); for(int i = 1; i <= n; i++) { int u = b[i].y; vis[u] = 1; for(int j = 0; j < to[u].size(); j++) { int v = to[u][j]; if(!vis[v] || found(u)==found(v)) continue; unite(u,v); } } for(int i = 1; i <= n; i++) { if(fa[i] == i) { ans += w[i]; } } printf("%lld\n",ans); } }