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;
}
浙公网安备 33010602011771号