Portals (USACO Gold 2021 US Open)

Portals (USACO Gold 2021 US Open)

这个题初看没有头绪,但仔细画图之后可以找到规律,之后中就挺直观的MST算法。

按图的结构,沿着连通的边走,可以走出一些回到原点的环,比如sample中有三个互不重合的环:

1-2-3-4 (回到1), 5-6-7, 8-9-10

这是因为每个portal是正好连接两个点(出现两次)。

每一个跨越两个环的vertex,通过调整之后,都可以把这两个环连接起来,变成一个环。

所以问题就转化成了,有一系列的环(可以看成是节点),然后有一些代价不同的办法可以把环合并(可以看成是长度不同的边),问把所有环都合并成一个大环的最小代价是什么。这个其实就是问在新的图上面找最小生成树,让所有点之间都连通。

办法就是用union-find把环都找出来,然后用Kruskal一个个合并就行了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int n;
pair<int,int> c[100005];
int p[100005][4];
int fa[200005];
 
int find(int x) {return fa[x] == x ? x : fa[x] = find(fa[x]);}
int merge(int x, int y) { x = find(x); y = find(y); return x!=y ? (fa[x]=y, true) : false;}
 
int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d %d %d %d %d", &c[i].first, &p[i][0], &p[i][1], &p[i][2], &p[i][3]);
        c[i].second = i;
    }
    for (int i = 1; i<= 2*n; i++)
        fa[i] = i;
    for (int i = 0; i < n; i++) {
        merge(p[i][0], p[i][1]);
        merge(p[i][2], p[i][3]);
    }
    sort(c,c+n);
 
    // Kruskal to merge "circles"
    ll ans = 0;
    for (int i = 0; i < n; i++) {
        int x = c[i].second;
        if (merge(p[x][0], p[x][2])) {
            ans += c[i].first;
        }
    }
    printf("%lld", ans);
 
    return 0;
}