题目链接: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);
    }
}