BZOJ 5290
题意:见上。
一开始没看见必须选边…
然后发现是树形DP然后打……
之后发现我写假了
我们直接设$dp(u,i,j)$为$u$到根经过$i$条绿边,$j$条红边的最小代价
那么转移即
$$
dp[u][i][j] = \min(dp[lc[u]][i][j] + dp[rc[u]][i][ + 1], dp[lc[u]][i + 1][j] + dp[rc[u]][i][j])
$$
然后这题一个精髓是卡空间方法,我们维护一个dfn
,然后每次存两个孩子的值即可,具体可看代码。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#define ms(i, j) memset(i, j, sizeof i)
#define LL long long
#define db double
#define fir first
#define sec second
#define mp make_pair
using namespace std;
namespace flyinthesky {
const LL MAXN = 40000 + 5, INF = 1e16;
LL grn[MAXN], red[MAXN], dp[105][55][55];
LL n, dfn[MAXN];
LL a[MAXN], b[MAXN], c[MAXN];
void dfs(LL u, LL now) {
dfn[u] = now; // 卡空间
if (u > n - 1) {
for (LL i = 0; i <= 41; ++i)
for (LL j = 0; j <= 41; ++j) dp[dfn[u]][i][j] = c[u] * (a[u] + i) * (b[u] + j);
return ;
}
dfs(red[u], now + 1), dfs(grn[u], now + 2); // 卡空间
for (LL i = 0; i <= 41; ++i)
for (LL j = 0; j <= 41; ++j) {
dp[dfn[u]][i][j] = min(dp[dfn[grn[u]]][i][j] + dp[dfn[red[u]]][i][j + 1], dp[dfn[grn[u]]][i + 1][j] + dp[dfn[red[u]]][i][j]);
}
}
void clean() {
ms(red, 0), ms(grn, 0);
for (int i = 0; i <= 101; ++i)
for (int j = 0; j <= 51; ++j)
for (int k = 0; k <= 51; ++k) dp[i][j][k] = INF;
}
int solve() {
clean();
cin >> n;
for (LL s, t, i = 1; i < n; ++i) {
scanf("%lld%lld", &s, &t);
if (s < 0) s = -s + n - 1;
if (t < 0) t = -t + n - 1;
grn[i] = s, red[i] = t;
}
for (LL i = 1; i <= n; ++i) scanf("%lld%lld%lld", &a[i + n - 1], &b[i + n - 1], &c[i + n - 1]);
dfs(1, 1);
LL ans = INF;
for (LL i = 0; i <= 41; ++i)
for (LL j = 0; j <= 41; ++j) ans = min(ans, dp[dfn[1]][i][j]);
cout << ans;
return 0;
}
}
int main() {
flyinthesky::solve();
return 0;
}