CF1023F Mobile Phone Network 题解

发布时间 2023-08-24 14:55:18作者: User-Unauthorized

题意

给出 \(n\) 个点,\(k\) 条未钦定边权的边和 \(m\) 条已钦定边权的边,要求为这 \(k\) 条未指定边权的边分配权值使其均在图的最小生成树中且最大化这 \(k\) 条边的边权之和。

\(1 \le n,k,m \le 5 \times 10^5\))。

题解

首先满足要求这 \(k\) 条边均在最小生成树中,考虑将这 \(k\) 条边的权值置为极小值后建立最小生成树。然后考虑最大化边权之和。

此时需要考虑的是非树边对树边边权的限制,若有一条非树边 \(\left(a, b\right)\),边权为 \(w\),那么可以得出在最小生成树上的路径 \(a \rightarrow b\) 上的边边权均要满足不大于 \(w\),否则其将被非树边 \(\left(a, b\right)\) 替代。

发现这一过程为树链取最小值,使用树链剖分即可。如果存在未指定边权的边在处理完所有限制后没有限制到,输出 \(-1\) 即可。在 \(n,m,k\) 同阶的情况下总复杂度为 \(\mathcal{O}(n \log^2 n)\),可以通过本题。

Code

#include <bits/stdc++.h>

typedef int valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;

constexpr valueType MAX = INT_MAX >> 1;

class DSU {
private:
    valueType N;

    std::vector<int> father, size;

public:
    explicit DSU(valueType n) : N(n), father(N, 0), size(N, 0) {
        std::iota(father.begin(), father.end(), 0);

        std::fill(size.begin(), size.end(), 1);
    }

    void resize(valueType n) {
        N = n;

        father.resize(N, 0);
        size.resize(N);

        std::iota(father.begin(), father.end(), 0);

        std::fill(size.begin(), size.end(), 1);
    }

    int find(int x) {
        return father[x] == x ? x : father[x] = find(father[x]);
    }

    void unite(int x, int y) {
        x = find(x), y = find(y);

        if (x == y) return;

        if (size[x] < size[y]) std::swap(x, y);

        father[y] = x;
        size[x] += size[y];
    }

    bool connected(int x, int y) {
        return find(x) == find(y);
    }

    valueType count(valueType n) {
        return size[find(n)];
    }
};

class Tree {
public:
    typedef ValueVector container;

private:
    valueType N;

    container data;

    void push(valueType id) {
        if (data[id] != MAX) {
            data[id << 1] = std::min(data[id << 1], data[id]);
            data[id << 1 | 1] = std::min(data[id << 1 | 1], data[id]);
            data[id] = MAX;
        }
    }

public:
    explicit Tree(valueType n) : N(n), data((N << 2) + 10, MAX) {}

    void resize(valueType n) {
        N = n;

        data.resize((N << 2) + 10, MAX);
    }

    void update(valueType l, valueType r, valueType key) {
        if (l > r)
            return;

        update(1, 1, N, l, r, key);
    }

    container ans() {
        container result(N + 1, MAX);

        query(1, 1, N, result);

        return result;
    }

private:
    void update(valueType id, valueType nodeL, valueType nodeR, valueType queryL, valueType queryR, valueType key) {
        if (queryL <= nodeL && nodeR <= queryR) {
            data[id] = std::min(data[id], key);

            return;
        }

        valueType const mid = (nodeL + nodeR) >> 1;

        push(id);

        if (queryL <= mid)
            update(id << 1, nodeL, mid, queryL, queryR, key);

        if (queryR > mid)
            update(id << 1 | 1, mid + 1, nodeR, queryL, queryR, key);
    }

    void query(valueType id, valueType l, valueType r, container &result) {
        if (l == r) {
            result[l] = data[id];

            return;
        }

        valueType const mid = (l + r) >> 1;

        push(id);

        query(id << 1, l, mid, result);
        query(id << 1 | 1, mid + 1, r, result);
    }
};

struct Edge {
    valueType from, to, weight;

    bool tag;

    Edge(valueType from, valueType to, valueType weight, bool tag) : from(from), to(to), weight(weight), tag(tag) {}

    bool operator<(Edge const &obj) const {
        return weight < obj.weight;
    }
};

typedef std::vector<Edge> EdgeVector;

valueType N, K, M;
ValueVector dfn, father, depth, size, son, top;
ValueMatrix G;
Tree tree(N + 1);

void dfs(valueType x, valueType from) {
    father[x] = from;
    depth[x] = depth[from] + 1;
    size[x] = 1;

    for (auto const &iter: G[x]) {
        if (iter == from)
            continue;

        dfs(iter, x);

        size[x] += size[iter];

        if (son[x] == 0 || size[iter] > size[son[x]])
            son[x] = iter;
    }
}

valueType dfsCount = 0;

void build(valueType x, valueType from, valueType TOP) {
    dfn[x] = ++dfsCount;
    top[x] = TOP;

    if (son[x] == 0)
        return;

    build(son[x], x, TOP);

    for (auto const &iter: G[x]) {
        if (iter == from || iter == son[x])
            continue;

        build(iter, x, iter);
    }
}

void modify(valueType x, valueType y, valueType key) {
    while (top[x] != top[y]) {
        if (depth[top[x]] < depth[top[y]])
            std::swap(x, y);

        tree.update(dfn[top[x]], dfn[x], key);

        x = father[top[x]];
    }

    if (depth[x] > depth[y])
        std::swap(x, y);

    tree.update(dfn[x] + 1, dfn[y], key);
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

//#ifndef LOCAL_STDIO
//    freopen("trade.in", "r", stdin);
//    freopen("trade.out", "w", stdout);
//#endif

    std::cin >> N >> K >> M;

    G.resize(N + 1);
    dfn.resize(N + 1);
    father.resize(N + 1);
    depth.resize(N + 1);
    size.resize(N + 1);
    son.resize(N + 1);
    top.resize(N + 1);
    tree.resize(N + 1);

    EdgeVector edge;

    edge.reserve(K + M);

    for (valueType i = 0; i < K; ++i) {
        valueType u, v;

        std::cin >> u >> v;

        edge.emplace_back(u, v, 0, true);
    }

    for (valueType i = 0; i < M; ++i) {
        valueType u, v, w;

        std::cin >> u >> v >> w;

        edge.emplace_back(u, v, w, false);
    }

    std::sort(edge.begin() + K, edge.end());

    EdgeVector treeEdge, remainEdge;

    treeEdge.reserve(N);
    remainEdge.reserve(K + M);

    DSU dsu(N + 1);

    for (auto const &iter: edge) {
        if (dsu.connected(iter.from, iter.to)) {
            remainEdge.push_back(iter);
        } else {
            dsu.unite(iter.from, iter.to);
            treeEdge.push_back(iter);

            G[iter.from].emplace_back(iter.to);
            G[iter.to].emplace_back(iter.from);
        }
    }

    ValueVector root;

    for (valueType i = 1; i <= N; ++i) {
        if (dfn[i] == 0) {
            dfs(i, 0);
            build(i, 0, i);
            root.push_back(i);
        }
    }

    for (auto const &iter: root)
        tree.update(dfn[iter], dfn[iter], 0);

    for (auto &iter: treeEdge) {
        if (!iter.tag) {
            if (depth[iter.from] > depth[iter.to])
                std::swap(iter.from, iter.to);

            tree.update(dfn[iter.to], dfn[iter.to], 0);
        }
    }

    for (auto const &iter: remainEdge) {
        modify(iter.from, iter.to, iter.weight);
    }

    auto const &result = tree.ans();

    long long ans = 0;
    
    for (valueType i = 1; i <= N; ++i) {
        if (result[dfn[i]] == MAX) {
            std::cout << -1 << std::endl;

            std::exit(0);
        }

        ans += result[dfn[i]];
    }

    std::cout << ans << std::endl;

    std::exit(0);
}