2020(ICPC)亚洲区域赛(南京) M.Monster Hunter
2020(ICPC)亚洲区域赛(南京) M.Monster Hunter
题意
给定一颗大小为\(n\)的有根树,每个结点都有点权\(hp[i]\),选取点的代价是\(hp[i] + \sum_{son of i} hp[j]\),且其父亲也被选
现在可以在这棵树中删除\(0-n\)个点,问删除这些点的情况下最小的代价分别是多少
\[2 \leq n \leq 2\times 10^3\\
1\leq hp\leq 10^9
\]
分析
树上背包问题
令\(f[i][j][k]\)表示以\(i\)为根的子树,\(i\)是否选取,子树中一共选取\(j\)个点的情况下的最小代价是多少
转移方程
\[f[x][j + k][1] = min(f[x][j][1]+min(f[v][k][0],f[v][k][1]+hp[v]))\\
f[x][j + k][0] = min(f[x][j][0] + min(f[v][k][0],f[v][k][1]))
\]
复杂度的正确性在于任意两点只被枚举到一次,因此复杂度\(O(n^2)\)
代码
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
ll rd(){
ll x = 0;
int f = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
const int maxn = 4005;
int n,m;
ll p[maxn],hp[maxn];
vector<int> e[maxn];
ll f[maxn][maxn][2];
int siz[maxn];
void dfs(int x){
f[x][0][0] = 0;
f[x][1][1] = hp[x];
siz[x] = 1;
for(auto v:e[x]){
dfs(v);
for(int j = siz[x];j >= 0;j--){
for(int k = siz[v];k >= 0;k--){
f[x][j + k][1] = min(f[x][j + k][1],f[x][j][1] + min(f[v][k][0],f[v][k][1] + hp[v]));
f[x][j + k][0] = min(f[x][j + k][0],f[x][j][0] + min(f[v][k][0],f[v][k][1]));
}
}
siz[x] += siz[v];
}
}
void solve(){
int n = rd();
for(int i = 1;i <= n;i++) e[i].clear();
for(int i = 1;i <= n;i++)
for(int j = 0;j <= n;j++)
f[i][j][0] = f[i][j][1] = 1e18 + 7;
for(int i = 2;i <= n;i++){
p[i] = rd();
e[p[i]].pb(i);
}
for(int i = 1;i <= n;i++)
hp[i] = rd();
dfs(1);
for(int i = n;i >= 0;i--)
printf("%lld ",min(f[1][i][0],f[1][i][1]));
puts("");
}
int main(){
int T = rd();
while(T--)
solve();
}