1 条题解

  • 0
    @ 2026-10-26 14:35:24

    思路与算法

    每个节点连向其最讨厌的人,形成若干基环树。先按入度0剥叶,累积不选该节点zero及选该节点one两种收益:父不选可取孩子两态最大,父选只能取孩子不选。剩余每个有向环按顺序做线性独立集DP,分别强制首点选/不选,首点选时末点必须不选,取优后汇总所有分量。

    正确性说明

    函数图每个分量恰有一个环,剥叶顺序保证树孩子都先聚合。独立集的选/不选转移完整表达相邻不能同时选。环的唯一额外约束是首尾不能同时选,用两次首点状态覆盖全部合法环方案。分量之间无约束,最优收益相加。互相讨厌的二环虽对应重复无向约束,环DP仍只禁止同时选,不重复计算权值。

    复杂度

    时间O(N)、空间O(N),初始化入度和两收益数组O(N)包含在内。N≤10^6,权重≤10^6,总收益≤10^12,用long long;迭代剥叶和环DP不使用百万层递归。

    易错点

    不能只把图当普通树,也不能忽略二环。剥叶只在入度归0时入队,剩余正入度才是环点。独立答案对小N枚举全部子集;满规模二环对、长链和偶环分别用成对最大值、路径DP及均匀环公式验证。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n;
        cin >> n;
        vector<int> to(n), deg(n);
        vector<long long> zero(n), one(n);
        for (int i = 0; i < n; ++i) {
            cin >> one[i] >> to[i];
            --to[i];
            ++deg[to[i]];
        }
        vector<int> q;
        q.reserve(n);
        for (int i = 0; i < n; ++i)
            if (!deg[i])
                q.push_back(i);
        for (size_t at = 0; at < q.size(); ++at) {
            int u = q[at], v = to[u];
            zero[v] += max(zero[u], one[u]);
            one[v] += zero[u];
            if (--deg[v] == 0)
                q.push_back(v);
        }
        long long answer = 0, NEG = -(1LL << 60);
        for (int start = 0; start < n; ++start)
            if (deg[start]) {
                vector<int> cycle;
                int u = start;
                do {
                    cycle.push_back(u);
                    deg[u] = 0;
                    u = to[u];
                } while (u != start);
                long long best = 0;
                for (int first = 0; first < 2; ++first) {
                    long long a = first ? NEG : zero[start], b = first ? one[start] : NEG;
                    for (size_t i = 1; i < cycle.size(); ++i) {
                        int v = cycle[i];
                        long long na = max(a, b) + zero[v], nb = a + one[v];
                        a = na;
                        b = nb;
                    }
                    best = max(best, first ? a : max(a, b));
                }
                answer += best;
            }
        cout << answer << '\n';
    }
    
    • 1

    信息

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