P9634 [ICPC2020 Nanjing R] Monster Hunter 题解
考虑树上背包。但是直接设 表示以 为根的子树中用了 次魔法难以转移,问题在于难以确定 的儿子是否呗魔法标记。
于是加一维, 表示以 为根的子树中用了 次魔法, 点被魔法标记了或没有被标记。 表示被标记, 表示没有被标记。
转移时朴素地类似树上背包转移即可。
时间复杂度 。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
using namespace std;
const int N = 2e3 + 5;
long long f[N][N][2], hp[N], tmp[N][N][2];
// f[i][j][0]: 根不存在,f[i][j][1]:根存在
vector<int> G[N];
int t, n;
int sz[N];
void dfs(int u, int fa)
{
f[u][1][0] = 0LL;
f[u][0][1] = hp[u];
sz[u] = 1;
int c = 0;
for (auto& j : G[u])
{
if (j == fa) continue;
c++;
dfs(j, u);
for (int i = 0; i <= sz[u] + sz[j]; i++) tmp[u][i][0] = tmp[u][i][1] = (long long)1e18;
for (int i = 0; i <= sz[j]; i++) // 枚举子树 j 删的个数
{
for (int k = 0; k <= sz[u]; k++) // 枚举当前子树删的个数
{
int tot = i + k;
tmp[u][tot][0] = min(tmp[u][tot][0], min(f[j][i][0], f[j][i][1]) + f[u][k][0]);
}
}
for (int i = 0; i <= sz[j]; i++) // 枚举子树 j 删的个数
{
for (int k = 0; k <= sz[u]; k++) // 枚举当前子树删的个数
{
int tot = i + k;
tmp[u][tot][1] = min(tmp[u][tot][1], min(f[j][i][0], f[j][i][1] + hp[j]) + f[u][k][1]);
}
}
sz[u] += sz[j];
for (int i = 0; i <= sz[u]; i++) f[u][i][0] = tmp[u][i][0], f[u][i][1] = tmp[u][i][1];
}
if (f[u][sz[u]][0] != 0) cout << "error: " << u << "\n";
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0);
cin >> t;
while (t--)
{
cin >> n;
for (int i = 1; i <= n; i++) G[i].clear(), G[i].shrink_to_fit();
for (int i = 2; i <= n; i++)
{
int fa;
cin >> fa;
G[fa].emplace_back(i);
}
for (int i = 1; i <= n; i++) cin >> hp[i];
for (int i = 1; i <= n; i++)
{
for (int j = 0; j <= n; j++) f[i][j][0] = f[i][j][1] = (long long)1e18;
}
dfs(1, 0);
for (int i = 0; i <= n; i++) cout << min(f[1][i][0], f[1][i][1]) << " ";
cout << "\n";
}
return 0;
}

浙公网安备 33010602011771号