真·普及 DP
令 \(f_{x,y,z}\) 为在 \(i\) 点,有 \(y\) 条公路没修,有 \(z\) 条铁路没修
\[f_{x,y,z}=\max\{f_{s_i,y+1,z}+f_{t_i,y,z},f_{s_i,y,z}+f_{s_i,y,z+1}\}\]
由于树的高是保证的,所以并不会被卡空间
可以先把 \(dfn\) 跑出来后倒着推,也可以直接 \(dfs\)
注意下细节
#include <iostream>
#include <cstdio>
#include <cstring>
typedef long long ll;
const int MaxN = 4e4 + 5;
int N;
int Ch[MaxN][2], A[MaxN], B[MaxN], C[MaxN];
ll Dp[MaxN][41][41];
inline void dfs(int x, int s1, int s2)
{
if(x >= N)
{
for(int i = 0; i <= s1; ++i)
for(int j = 0; j <= s2; ++j)
Dp[x][i][j] = 1ll * C[x] * (A[x] + i) * (B[x] + j);
return;
}
dfs(Ch[x][0], s1 + 1, s2);
dfs(Ch[x][1], s1, s2 + 1);
for(int i = 0; i <= s1; ++i)
for(int j = 0; j <= s2; ++j)
Dp[x][i][j] = std::min(Dp[Ch[x][0]][i + 1][j] + Dp[Ch[x][1]][i][j], Dp[Ch[x][0]][i][j] + Dp[Ch[x][1]][i][j + 1]);
}
int main()
{
int x, y;
scanf("%d", &N);
for(int i = 1; i < N; ++i)
{
scanf("%d %d", &x, &y);
if(x < 0) x = -x + N - 1;
if(y < 0) y = -y + N - 1;
Ch[i][0] = x;
Ch[i][1] = y;
}
for(int i = 1; i <= N; ++i)
{
x = i + N - 1;
scanf("%d %d %d" ,&A[x], &B[x], &C[x]);
}
dfs(1, 0, 0);
printf("%lld\n", Dp[1][0][0]);
return 0;
}