社区讨论

SOS

P14637[NOIP2025] 树的价值参与者 2已保存回复 1

讨论操作

快速查看讨论及其快照的属性,并进行相关操作。

当前回复
1 条
当前快照
1 份
快照标识符
@mk8b0f1b
此快照首次捕获于
2026/01/10 20:51
上个月
此快照最后确认于
2026/01/13 22:20
上个月
查看原帖
CPP
#include <bits/stdc++.h>
using namespace std;

const long long INF = 1e18;

void solve() {
    int n, m;
    cin >> n >> m;
    vector<vector<int>> g(n + 1);
    for (int i = 2; i <= n; i++) {
        int p;
        cin >> p;
        g[p].push_back(i);
    }

    vector<int> sz(n + 1), depth(n + 1), lim(n + 1);
    function<void(int)> dfs_sz = [&](int u) {
        sz[u] = 1;
        for (int v : g[u]) {
            depth[v] = depth[u] + 1;
            dfs_sz(v);
            sz[u] += sz[v];
        }
        // lim[u] 表示以 u 为根的子树中,可能的最大 mex 值(受子树大小和深度限制)
        lim[u] = min(sz[u], m - depth[u] + 1);
        // 由于深度不超过 m,所以 m - depth[u] + 1 >= 1,因此 lim[u] >= 1
        // 但为了安全,我们确保 lim[u] 至少为 0
        if (lim[u] < 0) lim[u] = 0;
    };
    depth[1] = 0;
    dfs_sz(1);

    vector<vector<long long>> f(n + 1);

    function<void(int)> dfs = [&](int u) {
        if (g[u].empty()) {
            f[u].assign(lim[u] + 1, -INF);
            f[u][0] = 0;
            if (lim[u] >= 1) f[u][1] = 1;
            return;
        }

        for (int v : g[u]) {
            dfs(v);
        }

        long long sum0 = 0;
        for (int v : g[u]) {
            sum0 += f[v][0];
        }

        f[u].assign(lim[u] + 1, -INF);
        // 情况1:所有子节点的 mex 都为 0
        for (int i = 0; i <= lim[u]; i++) {
            // 需要满足子树大小足够容纳 i 个不同的权值
            if (i <= sz[u]) {
                f[u][i] = i + sum0;
            }
        }

        // 情况2:恰好一个子节点的 mex 大于 0
        for (int v : g[u]) {
            // 预处理 val[j] = sum0 - f[v][0] + f[v][j]
            vector<long long> val(lim[v] + 1, -INF);
            for (int j = 0; j <= lim[v]; j++) {
                if (f[v][j] > -INF / 2) {
                    val[j] = sum0 - f[v][0] + f[v][j];
                }
            }

            // 对于每个 j(j>0),更新对应的 i 区间
            for (int j = 1; j <= lim[v]; j++) {
                if (val[j] < -INF / 2) continue;
                int left = j;
                int right = min(lim[u], j + sz[u] - sz[v]);
                if (left > right) continue;
                // 直接更新区间内的每个 i
                for (int i = left; i <= right; i++) {
                    long long cand = i + val[j];
                    if (cand > f[u][i]) {
                        f[u][i] = cand;
                    }
                }
            }
        }

        // 释放子节点的内存,避免 MLE
        for (int v : g[u]) {
            vector<long long>().swap(f[v]);
        }
    };

    dfs(1);

    long long ans = 0;
    for (int i = 0; i <= lim[1]; i++) {
        if (f[1][i] > ans) ans = f[1][i];
    }
    cout << ans << "\n";
}

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

    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

回复

1 条回复,欢迎继续交流。

正在加载回复...