题目链接: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);
}
}
京公网安备 11010502036488号