← 返回题目页

A1541 兽径管理

标程

// A1541 兽径管理 —— 增量维护最小生成森林
//
// 每周新增一条兽径,要求输出「目前已知的所有兽径」构成的图的最小生成树权值;
// 若图不连通则输出 -1。
//
// 增量做法:维护当前图的最小生成森林。
//   1) 新兽径的两端属于不同连通块:直接把它加进生成森林,合并两个连通块;
//   2) 两端已经连通:新兽径与树上 u-v 路径构成一个环,
//      环上最重的边一定在 u-v 路径上;若路径上最重的边比新兽径更重,
//      就用新兽径替换它(最小生成树的「换边」性质)。
// 每次只改动一条边,答案立刻更新。

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 205;

int n, weeks;
int fa[MAXN];
vector<pair<int, int>> g[MAXN]; // 当前生成森林的邻接表:(邻居, 边权)
int edgeCnt = 0;                // 生成森林的边数
long long total = 0;            // 生成森林的边权和

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

void addEdge(int u, int v, int w) {
    g[u].push_back({v, w});
    g[v].push_back({u, w});
    edgeCnt++;
    total += w;
}

void removeEdge(int u, int v, int w) {
    for (size_t i = 0; i < g[u].size(); i++) {
        if (g[u][i].first == v) {
            g[u].erase(g[u].begin() + i);
            break;
        }
    }
    for (size_t i = 0; i < g[v].size(); i++) {
        if (g[v][i].first == u) {
            g[v].erase(g[v].begin() + i);
            break;
        }
    }
    edgeCnt--;
    total -= w;
}

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

    cin >> n >> weeks;
    for (int i = 1; i <= n; i++) fa[i] = i;

    for (int week = 1; week <= weeks; week++) {
        int u, v, w;
        cin >> u >> v >> w;

        if (find(u) != find(v)) {
            // 两端不连通:直接连上,生成森林多一条边
            fa[find(u)] = find(v);
            addEdge(u, v, w);
        } else {
            // 两端已连通:在 u-v 路径上找最重的边
            vector<int> par(n + 1, 0), pw(n + 1, 0);
            vector<int> stk;
            stk.push_back(u);
            par[u] = -1;
            while (!stk.empty()) {
                int x = stk.back();
                stk.pop_back();
                for (auto [y, ww] : g[x]) {
                    if (par[y] == 0) {
                        par[y] = x;
                        pw[y] = ww;
                        stk.push_back(y);
                    }
                }
            }
            int mx = -1, child = -1;
            for (int x = v; x != u; x = par[x]) {
                if (pw[x] > mx) {
                    mx = pw[x];
                    child = x;
                }
            }
            if (mx > w) {
                // 环上最重的边在新兽径这一侧:换掉它
                removeEdge(par[child], child, mx);
                addEdge(u, v, w);
            }
        }

        if (edgeCnt == n - 1) {
            cout << total << '\n';
        } else {
            cout << -1 << '\n';
        }
    }
    return 0;
}

动画演示