1 条题解

  • 0
    @ 2026-10-26 1:01:10

    思路与算法

    主要边构成树。先统计每条主要边被多少条附加边跨越:对附加边(u,v),给u、v各加1,给LCA(u,v)减2,再按逆序把差分值累加到父节点。每个非根节点的累加值就是父边的覆盖数c。逐条主要边计数:c=0时贡献M,c=1时贡献1,c≥2时贡献0。

    正确性说明

    删掉一条主要边后,树分成两块,只有跨越这两块的附加边能够继续连接它们。若跨越数为0,第一刀已断开,第二刀仍必须选择任意一条附加边,所以有M种;若只有1条,第二刀只能删掉它;若至少2条,再删1条仍连通。树上边差分恰好让每条附加边在其对应树路径上贡献1,所以覆盖数和最后的方案数都正确。

    复杂度

    倍增预处理与M次LCA合计 O((N+M) log N),逆序累加 O(N),空间 O(N log N)。

    易错点

    覆盖数为0时也必须执行第二刀,不能只贡献1。附加边可能重合,也可能两端相同,按独立的附加边计数;同点路径对树边无贡献。用迭代遍历生成逆序,避免10万点长链递归过深。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n, m;
        cin >> n >> m;
        vector<vector<int>> g(n);
        for (int i = 1; i < n; i++) {
            int u, v;
            cin >> u >> v;
            --u;
            --v;
            g[u].push_back(v);
            g[v].push_back(u);
        }
        const int K = 18;
        vector<array<int, K>> up(n);
        vector<int> depth(n), order(1, 0), parent(n, -1), delta(n);
        parent[0] = 0;
        for (size_t at = 0; at < order.size(); at++) {
            int u = order[at];
            for (int v : g[u])
                if (v != parent[u]) {
                    parent[v] = u;
                    depth[v] = depth[u] + 1;
                    order.push_back(v);
                }
        }
        for (int v : order) {
            up[v][0] = parent[v];
            for (int k = 1; k < K; k++)
                up[v][k] = up[up[v][k - 1]][k - 1];
        }
        auto lca = [&](int u, int v) {
            if (depth[u] < depth[v])
                swap(u, v);
            int difference = depth[u] - depth[v];
            for (int k = 0; k < K; k++)
                if (difference >> k & 1)
                    u = up[u][k];
            if (u == v)
                return u;
            for (int k = K - 1; k >= 0; k--)
                if (up[u][k] != up[v][k]) {
                    u = up[u][k];
                    v = up[v][k];
                }
            return parent[u];
        };
        for (int i = 0; i < m; i++) {
            int u, v;
            cin >> u >> v;
            --u;
            --v;
            int w = lca(u, v);
            delta[u]++;
            delta[v]++;
            delta[w] -= 2;
        }
        long long ans = 0;
        for (int i = n - 1; i > 0; i--) {
            int v = order[i];
            if (delta[v] == 0)
                ans += m;
            else if (delta[v] == 1)
                ans++;
            delta[parent[v]] += delta[v];
        }
        cout << ans << '\n';
    }
    
    • 1

    信息

    ID
    1632
    时间
    1000ms
    内存
    512MiB
    难度
    普及+
    标签
    递交数
    1
    已通过
    1
    上传者