let menu = ["Home", "Algorithms", "CodeHub", "VNOI Statistics"];

Overview
solution.md

Ta thấy đường đi có thông lượng lớn nhất giữa 2 máy chính là đường đi trên cây khung lớn nhất.

Vì vậy, bài này ta cần tìm cây khung lớn nhất, sau đó với mỗi cạnh (u,v) không thuộc cây khung, ta tìm trọng số nhỏ nhất trên đường đi từ u đến v trong cây khung. Để không bị quá thời gian, code hld.cpp dùng heavy-light decomposition, còn code tarjan.cpp dùng Tarjan’s offline LCA (Xem các tài liệu đính kèm bên trên).

tarjan.cpp
Open in Github Download
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
#define LL long long
#define BLACK true

struct edge { int c, u, v; };
bool operator < (const edge &a, const edge &b) {
    return a.c > b.c;
}

struct dsu {
    int *cha, *rank;
    dsu(int n): cha(new int[n+1]) {
        for (int i=0; i<=n; i++) cha[i] = i;
    }
    int find(int u) {
        if (cha[u] != u) cha[u] = find(cha[u]);
        return cha[u];
    }
    void join(int u, int v) {
        if (rand()&1) swap(u, v);
        cha[find(u)] = find(v);
    }
};

namespace lca {
    LL res = 0;
    int n, cha[100001], c[100001];
    bool color[100001];
    vector<int> queries[100001];
    struct pack { int c, v; };
    vector<pack> ke[100001];
    struct queue_type { int u, v; };
    vector<queue_type> queue[100001];
    int find(int u) {
        if (cha[u] != u) {
            int tmp = cha[u];
            cha[u] = find(cha[u]);
            c[u] = min(c[u], c[tmp]);
        }
        return cha[u];
    }
    void dfs(int u, int dad) {
        cha[u] = u;
        for (pack p: ke[u]) if (p.v != dad) {
            dfs(p.v, u);
            cha[p.v] = u;
            c[p.v] = p.c;
        }
        color[u] = BLACK;
        for (int v: queries[u]) if (color[v] == BLACK) {
            queue[find(v)].push_back({u, v});
        }
        for (auto q: queue[u]) {
            find(q.u), find(q.v);
            res += min(c[q.u], c[q.v]);
        }
    }
    void solve() {
        for (int i=1; i<=n; i++) c[i] = 10000000;
        dfs(1, 0);
    }
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    int n, m; cin >> n >> m;
    vector<edge> edges(m);
    for (edge &e: edges) cin >> e.u >> e.v >> e.c;
    sort(edges.begin(), edges.end());
    dsu d(n);
    LL res = 0;
    for (edge &e: edges) {
        if (d.find(e.u) != d.find(e.v)) {
            d.join(e.u, e.v);
            lca::ke[e.u].push_back({e.c, e.v});
            lca::ke[e.v].push_back({e.c, e.u});
        } else {
            res += e.c;
            lca::queries[e.u].push_back(e.v);
            lca::queries[e.v].push_back(e.u);
        }
    }
    lca::n = n;
    lca::solve();
    cout << lca::res - res;
    return 0;
}
hld.cpp
Open in Github Download
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;

#define LL long long
struct pack{ int u, v, c; };
typedef vector<vector<int>> dsk;

struct universe {
    vector<int> cha, rank;
    universe(int n): cha(n + 1), rank(n + 1, 0) {
        for (int i=1; i<=n; i++) cha[i] = i;
    }
    int find(int u) {
        if (cha[u] != u) cha[u] = find(cha[u]);
        return cha[u];
    }
    bool join(int u, int v) {
        if (u == v) return false;
        if (rank[u] == rank[v]) rank[u]++;
        if (rank[u] > rank[v]) cha[v] = u;
        else cha[u] = v;
        return true;
    }
};

inline void minimize(int &x, int y) {
    if (x > y) x = y;
}

struct Hld {
    dsk ke;
    vector<int> nChild, cha, bac, chainHead, chain, pos;
    vector<int> f, r, w;
    int n;
    Hld(int n, dsk &&ke): ke(ke),
    nChild(n + 1, 0), cha(n + 1), bac(n + 1),
    chainHead(1, 0), chain(n + 1), pos(n + 1),
    f(n+1, 1e9), r(n+1, 1e9), w(n+1, 1e9), n(n)
    {
        prepare(1);
        int nChain = 0, count = 0;
        hld(1, nChain, count);
    }

    int prepare(int u) {
        nChild[u] = 1;
        for (int i=0; i<(int)ke[u].size(); i++) if (nChild[ke[u][i]]) {
            ke[u][i] = ke[u].back();
            ke[u].pop_back();
            break;
        }
        for (auto v: ke[u]) {
            cha[v] = u;
            bac[v] = bac[u] + 1;
            nChild[u] += prepare(v);
        }
        return nChild[u];
    }
    void hld(int u, int &nChain, int &count) {
        if (!chainHead[nChain]) chainHead[nChain] = u;
        chain[u] = nChain;
        pos[u] = ++count;
        int con = 0;
        for (auto v: ke[u]) if (!con || nChild[con] < nChild[v]) con = v;
        if (con) hld(con, nChain, count);
        for (auto v: ke[u]) if (v != con) {
            nChain++;
            chainHead.push_back(0);
            hld(v, nChain, count);
        }
    }

    void bitUp(int x, int i) {
        r[i] = x;
        while(i <= n) minimize(f[i], x), i += i & (-i);
    }
    int bitGet(int u, int v) {
        if (u > v) swap(u, v);
        u += 1;
        int res = 1e9;
        while (u <= v) {
            if (u <= (v - v & (-v))) {
                minimize(res, f[v]);
                v -= v & (-v);
            } else {
                minimize(res, r[v]);
                v -= 1;
            }
        }
        return res;
    }

    void update(int u, int v, int c) {
        if (chain[u] != chain[v]) {
            if (cha[u] == v) w[u] = c;
            else w[v] = c;
        } else {
            if (cha[u] == v) bitUp(c, pos[u]);
            else bitUp(c, pos[v]);
        } 
    }

    int query(int u, int v) {
        int res = 1e9;
        while (chain[u] != chain[v]) {
            int hu = chainHead[chain[u]], hv = chainHead[chain[v]];
            if (bac[hu] > bac[hv]) swap(u, v), swap(hu, hv);
            minimize(res, bitGet(pos[v], pos[hv]));
            minimize(res, w[hv]);
            v = cha[hv];
        }
        minimize(res, bitGet(pos[u], pos[v]));
        return res;
    }
};

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    int n, m; cin >> n >> m;
    universe s(n);
    vector<pack> edges(m);
    for (auto &e: edges) cin >> e.u >> e.v >> e.c;
    sort(edges.begin(), edges.end(), [](pack &a, pack &b){return a.c > b.c;});
    vector<pack> q, tree;
    dsk ke(n + 1);
    for (auto &e: edges) {
        if (s.join(s.find(e.u), s.find(e.v))) {
            ke[e.u].push_back(e.v);
            ke[e.v].push_back(e.u);
            tree.push_back(e);
        } else q.push_back(e);
    }
    Hld hld(n, move(ke));
    for (auto &x: tree) hld.update(x.u, x.v, x.c);
    LL res = 0;
    for (auto &q: q) res += (LL)(hld.query(q.u, q.v) - q.c);
    cout << res;
    return 0;
}
Comments