Loading

loj#566. 「LibreOJ Round #10」yanQval 的生成树

\(\mu\) 取值即所选边权的中位数。

把每条边拆成两条(黑边和白边),边权分别为 \(\mu - w_i\)\(w_i - \mu\),要求黑边和白边各选 \(\left\lfloor\dfrac{n-1}2\right\rfloor\) 条,求最大生成树。

可以直接 wqs 二分,时间复杂度 \(\mathcal O(nm \log w~\alpha(n))\)

把所有边的边权同时加上一个 \(\mu\),则黑边边权为 \(2\mu - w_i\),白边边权为 \(w_i\),现在 \(\mu\) 的取值只会影响黑边的边权,wqs 二分的斜率 \(k\) 也只会影响黑边的边权,所以可以放到一起二分,时间复杂度 \(\mathcal O(m \log w~\alpha(n))\)

一个值得注意的点是无论 \(n\) 的奇偶,取的黑边和白边的条数都是 \(\left\lfloor\dfrac{n-1}2\right\rfloor\)

\(n\) 是奇数的时候显然,但 \(n\) 是偶数时,生成树上一定会有一条边无法确定颜色,否则会影响 wqs 二分中的答案,但本题下任一一棵 \(m\) 条边的生成树势必存在一棵与其离差相同的 \(m - 1\) 边的生成森林,所以直接做 \(n - 2\) 条边的最大生成森林即可。

代码

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

constexpr int N = 2e5 + 10, M = 5e5 + 10;

int n, m, eds;
ll res, ans;

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

namespace DSU {
    int fa[N], sz[N];
    inline void init() {iota(fa + 1, fa + n + 1, 1); memset(sz + 1, 0, n << 2);}
    inline int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
    inline bool merge(int x, int y) {
        int fx = find(x), fy = find(y);
        if (fx == fy) return 0;
        if (sz[fx] < sz[fy]) swap(fx, fy);
        fa[fy] = fx, sz[fx] += sz[fy];
        return 1;
    }
}

bool check(int C) {
    DSU::init();
    int cnt = res = 0, wcnt = 0, l = 1, r = m;
    while (l <= r) {
        Edge cur; bool white = 0;
        if (e[l].w > C - e[r].w) cur = e[l++];
        else cur = e[r--], white = 1, cur.w = C - cur.w;
        if (DSU::merge(cur.u, cur.v)) {
            res += cur.w, wcnt += white;
            if (++cnt == (eds << 1)) break;
        }
    }
    res -= 1ll * eds * C;
    return wcnt >= eds;
}

int main() {
    ios_base::sync_with_stdio(0); cin.tie(nullptr), cout.tie(nullptr);
    cin >> n >> m; eds = (n - 1) >> 1; if (!eds) {cout << 0; return 0;}
    for (int i = 1; i <= m; i++) cin >> e[i].u >> e[i].v >> e[i].w;
    sort(e + 1, e + m + 1);
    int l = 0, r = 1e9;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check(mid)) r = mid - 1, ans = res;
        else l = mid + 1;
    }
    cout << ans;
    return 0;
}
posted @ 2024-05-17 11:30  Chy12321  阅读(67)  评论(0编辑  收藏  举报