2020 ICPC Taipei H.Optimization for UltraNet(二分 + 最小生成树)

题意:好长啊  求一棵生成树,首先满足最短边最大,其次使任意两点路径上的最短边之和最小。

思路:要使最短边取最大值,考虑二分最短边,判断当前能否生成一棵树,如果可以,考虑使任意两点路径上的最短边之和最小,也就是说,其余的边应该尽量小,即求最小生成树。建完图之后跑一遍任意两点路径上的最短边和就可以了。

参考

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const double eps = 1e-8;
const double PI = acos(-1.0);
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 27;
const int N = 2e4 + 7;
const int M = 1e6 + 7;

int n, m;
int head[N], tot, nxt[N], to[N], val[N], fa[N], siz[N];
int mi, id, X, Y;
bool vis[N];

struct Edge {
    int u, v, w;
    bool operator < (const Edge &a) const {
        return w < a.w;
    }
} edge[M];

void addedge(int u, int v, int w) {
    to[++tot] = v;
    nxt[tot] = head[u];
    val[tot] = w;
    head[u] = tot;
}

int Find(int x) {
    if(x == fa[x]) return x;
    return fa[x] = Find(fa[x]);
}

bool Union(int x, int y) {
    int xx = Find(x);
    int yy = Find(y);
    if(xx != yy) {
        fa[xx] = yy;
        return 1;
    }
    return 0;
}

void get(int x, int fa) {
    siz[x] = 1;
    for(int i = head[x]; i; i = nxt[i]) {
        int v = to[i], w = val[i];
        if(vis[i] || v == fa) continue;
        get(v, x);
        siz[x] += siz[v];
        if(w < mi) {
            mi = w;
            id = i;
            Y = v, X = x;
        }
    }
}

ll dfs(int x) {
    bool flag = 0;
    for(int i = head[x]; i; i = nxt[i]) {
        if(vis[i]) continue;
        flag = 1;
        break;
    }
    if(!flag) return 0;
    mi = 1e9, id = 0;
    get(x, -1);
    vis[id] = 1, vis[id ^ 1] = 1;
    ll num = 1ll * (siz[x] - siz[Y]) * siz[Y] * mi;
    int _x = X, _y = Y;
    return num + dfs(_x) + dfs(_y);
}

bool check(int pos, bool flag) {
    for(int i = 1; i <= n; ++i) fa[i] = i;
    int cnt = 0;
    for(int i = pos; i <= m; ++i) {
        int xx = Find(edge[i].u);
        int yy = Find(edge[i].v);
        int w = edge[i].w;
        if(Union(xx, yy)) {
            ///只对最后一次建图
            if(flag) {
                addedge(edge[i].u, edge[i].v, w);
                addedge(edge[i].v, edge[i].u, w);
            }
            cnt++;
        }
        if(cnt >= n - 1) return 1;
    }
    return 0;
}

int lower_bound(int l, int r) {
    while (l < r) {
        int mid = (l + r + 1) >> 1;
        if (check(mid, 0)) l = mid;
        else r = mid - 1;
    }
    return l;
}

int main() {
    tot = 1;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; ++i)
        scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w);
    sort(edge + 1, edge + m + 1);
    int minn = lower_bound(1, m);
    check(minn, 1);
    printf("%lld\n", dfs(1));
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43871207/article/details/112975613