P9111 [福建省队集训2019] 最大权独立集问题
考虑把贡献拆开,即计算每个点对答案的贡献。
设删除点
考虑树形 DP。我们发现,对于当前 DP 的点
注意到,本题的时间复杂度允许我们在背包之外再乘一个
- 方向为
u \to v :v 对u 没有额外的贡献,枚举v 可达的点数j ,有f_{u,i,k}+f_{v,j,j}+ d_u \times j \to f_{u,i+j,k} 。 - 方向为
v \to u :v 对u 有额外k 的贡献,枚举v 可达的点数j ,有f_{u,i,k} + f_{v,j,j+k} \to f_{u,i,k} 。
转移完之后我们需要补上
总时间复杂度
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
constexpr int N = 4e2 + 5;
int n, a[N], c[N], sz[N];
LL f[N][N][N], g[N][N][N];
vector <int> e[N];
void dfs(int u) {
sz[u] = 1;
for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) f[u][i][k] = -1e18;
for (int k = 1; k <= n; k++) f[u][1][k] = 1LL * a[u] * k;
for (auto v : e[u]) {
dfs(v);
static LL tmp[N][N];
for (int k = 1; k <= n; k++) for (int i = 1; i <= sz[u]; i++) tmp[i][k] = f[u][i][k], f[u][i][k] = -1e18;
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= sz[u]; i++) {
for (int j = 1; j <= sz[v]; j++) {
f[u][i + j][k] = max(f[u][i + j][k], tmp[i][k] + f[v][j][j]);
}
LL val = -1e18;
for (int j = 1; j <= min(sz[v], n - k); j++) val = max(val, f[v][j][j + k]);
f[u][i][k] = max(f[u][i][k], tmp[i][k] + val);
}
}
sz[u] += sz[v];
}
}
signed main() {
ios :: sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 2; i <= n; i++) {
cin >> c[i];
e[c[i]].push_back(i);
}
dfs(1);
LL ans = -1e18;
for (int i = 1; i <= n; i++) ans = max(ans, f[1][i][i]);
cout << ans << "\n";
return 0;
}