但是进一步观察如果根本不记起 u,就不需要给以 u 为起点的联想过程分配容量,因为不同的联想连通块可以依次处理,所以只需要考虑与 u 联想连通的部分占用的记忆容量。如果不记起 u,就可以独立处理各个儿子子树。所以可以合并容量限制和当前点是否要记忆的信息。只要容量参数大于等于 1 就代表着当前点是一定要记忆的。
但是现在又有一个问题,当前的代价(处理子树的根)受到它父节点选不选的影响。一般可能会想到再加一维规定父节点的选择情况。但实际上父节点是否被记起,并不会改变 u 子树内部(不含 u)的最优处理方式,只会影响进入这棵子树的方式和可用容量。所以我们在处理父节点的时候再决定儿子的转移。
设 f_{u,0} 表示不记起 u,完成其子树内所有题目的最小代价(特别的,如果 u 是必须记起的事件,
设 $f_{u,j}$($1\le j\le k$)表示记起 $u$,且 $u$ 子树内与 $u$ 联想连通的部分最多同时占用 $j$ 个记忆位置的最小代价。
注意 $f_{u,j}$ **不包含记起 $u$ 本身的代价**。
对于 $u$ 的儿子 $v$,定义:
$$
A_v=\min(f_{v,0},r_v+f_{v,k})
$$
表示不通过 $u$ 联想 $v$ 的最小代价。
分类讨论一下转移:
- $j=0$ 儿子只能选择回忆或者不回忆:
$$f_{u,0}=\sum_{v\in son(u)}A_v$$
- $j=1$ 只有一个记忆位置,无法通过 $u$ 联想儿子,因此:
$$f_{u,1}=\sum_{v\in son(u)}A_v$$
- $2\le j\le k$ 对于儿子 $v$,可以选择不通过 $u$ 联想,或者通过 $u$ 联想:
$$B_v=\min(A_v,t_v+f_{v,j-1})$$
由于处理最后一个通过 $u$ 联想的儿子时,可以在记起 $v$ 后立即忘记 $u$,因此该儿子可以使用 $j$ 个记忆位置。
枚举最后一个通过 $u$ 联想的儿子,得到:
$$f_{u,j}=\sum_{v\in son(u)}B_v+\min\left(0,\min_{v\in son(u)}(t_v+f_{v,j}-B_v)\right)$$
对于每棵树的根节点 $u$,只能选择不记起或直接回想:
$$
ans=\sum_{p_u=0}\min(f_{u,0},r_u+f_{u,k})
$$
由于 $p_i<i$,可以直接按照节点编号从大到小进行转移。
时间复杂度和空间复杂度都是 $O(nk)$。
## 代码
因为我做完才想起 $p_i<i$,所以我还是建树跑递归了。
```cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5, K = 11;
const ll inf = 4e18;
int n, m, k;
ll r[N], t[N], dp[N][K];
int p[N];
vector<int> tr[N];
void dfs(int u, int fa)
{
for (int v : tr[u])
{
dfs(v, u);
ll A = min(dp[v][0], dp[v][k] + r[v]);
dp[u][0] += A;
dp[u][1] += A;
}
for (int j = 2; j <= k; j++)
{
ll mnB = 0;
for (int v : tr[u])
{
ll A = min(dp[v][0], dp[v][k] + r[v]); // 不回忆/回忆
ll B = min(A, dp[v][j - 1] + t[v]); // A/联想
dp[u][j] += B;
mnB = min(mnB, dp[v][j] + t[v] - B);
}
dp[u][j] += mnB;
}
}
void solve()
{
cin >> n >> m >> k;
for (int i = 1; i <= n; i++)
tr[i].clear();
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++)
{
cin >> p[i];
tr[p[i]].push_back(i);
}
for (int i = 1; i <= n; i++)
cin >> r[i];
for (int i = 1; i <= n; i++)
cin >> t[i];
for (int i = 1; i <= m; i++)
{
int x;
cin >> x;
dp[x][0] = inf;
}
ll ans = 0;
for (int i = 1; i <= n; i++)
{
if (!p[i])
{
dfs(i, 0);
ans += min(dp[i][0], r[i] + dp[i][k]);
}
}
cout << ans << "\n";
}
int main()
{
cin.tie(0)->sync_with_stdio(0);
int T;
cin >> T;
while (T--)
solve();
return 0;
}
```