P9111 [福建省队集训2019] 最大权独立集问题

\(i\) 号点被删的时刻为 \(t_i\)。考虑把边定向,\(t\) 较小的点指向 \(t\) 较大的点。那么一个 \(d_i\) 会贡献 \(cnt_i\) 次,其中 \(cnt_i\) 为在定向后的树上 \(i\) 能到的节点数。

考虑直接刻画 \(cnt\)。注意到 \(cnt_u=1+\sum\limits_{u\to v}[cnt_u\ge cnt_v]cnt_v\)。同时这个条件也是充分的,每条边较大的 \(cnt\) 连向较小的 \(cnt\) 就是一个合法的方案。

于是可以树形 dp,\(dp_{i,j,k}\) 表示 \(i\) 号点,\(cnt_i=j\)\(\sum\limits_{to\in son(i)}[cnt_i\ge cnt_{to}]cnt_{to}=k\) 的最大贡献和。转移讨论 \(cnt_i\)\(cnt_{to}\) 的大小关系即可。

注意这里的 \(k\) 总是不超过 \(i\) 的子树大小,枚举 \(j\),暴力背包就是 \(O(n^3)\)

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

const ll inf = 1e18;
const int kN = 405;
int n;
array<int, kN> v, siz;
array<vector<int>, kN> g;
array<array<ll, kN>, kN> mx;
array<array<array<ll, kN>, kN>, kN> dp;

void Dfs(int x, int fa) {
  siz[x] = 1;
  for(int to : g[x]) Dfs(to, x), siz[x] += siz[to];
  mx[x].fill(-inf);
  for(auto &k : dp[x]) k.fill(-inf);
  for(int i = 1; i <= n; i++) {
    int cur = 0;
    dp[x][i][0] = (ll)i * v[x];
    for(int to : g[x]) {
      array<ll, kN> old = dp[x][i];
      dp[x][i].fill(-inf);
      for(int j = 0; j <= cur; j++) {
        dp[x][i][j] = max(dp[x][i][j], old[j] + mx[to][i]);
        for(int k = 1; k <= siz[to]; k++) {
          dp[x][i][j + k] = max(dp[x][i][j + k], old[j] + dp[to][k][k - 1]);
        }
      }
      cur = min(cur + siz[to], i - 1);
    }
  }
  for(int i = 1; i <= n; i++) {
    for(int j = 0; j < min(siz[x], i); j++) {
      mx[x][i - j - 1] = max(mx[x][i - j - 1], dp[x][i][j]);
    }
  }
}

int main() {
  // freopen("1.in", "r", stdin);
  // freopen("1.out", "w", stdout);
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n;
  for(int i = 1; i <= n; i++) cin >> v[i];
  for(int i = 2; i <= n; i++) {
    int fa;
    cin >> fa;
    g[fa].push_back(i);
  }
  Dfs(1, 0);
  cout << mx[1][0] << "\n";
  return 0;
}
posted @ 2025-04-19 08:00  CJzdc  阅读(15)  评论(0)    收藏  举报