题解
其实刚看着道题的时候是一脸懵逼的(雾)。
然后想到使用dp,但是依然一脸懵。
但是看到这个极其小的 N N N和装备数量限制让我明白加维是不可避免的。
首先可以发现,子树中的节点信息可以对子树的根节点产生影响,这一点和NOI2020D1T2有点像(但是那题我不会做,因为不太懂线段树合并),而本题的信息就是这个节点所代表的装备有多少是留下来作为价值贡献的,有多少是上交作为更高级武器的材料的,但是我们显然只需要记住其中一个,因为我们还需要记住这个子树中总共使用的金币数。
那么,设 f ( u , x , y ) f(u,x,y) f(u,x,y)表示子树 T ( u ) T(u) T(u)中,使用 y y y个金币,这颗子树中锻造出装备 u u u共 y y y件上交,剩余的所有装备全部作为价值贡献。同时,我们弄一些辅助变量来减少常数: l i m ( u ) lim(u) lim(u)表示装备 u u u最多可以锻造多少个, c s t ( u ) cst(u) cst(u)表示要锻造一个装备 u u u需要花掉多少金币。
显然,对于叶子节点,也就是低级装备,我们只需要枚举制造了多少个,其中上交了多少个即可。
而对于非叶子节点 u u u,首先,枚举总共锻造了多少个装备 u u u,设为 n u m num num,那么对于每一个儿子装备的需求量就确认了。接着对锻造了 n u m num num个装备的情况,做一次背包,接着更新上交 i ( 0 ≤ i ≤ n u m ) i(0\leq i\leq num) i(0≤i≤num)个的所有状态的dp值。
接着,对于所有没有父亲装备的节点,我们用背包对其进行合并,使用的值都是形如 f ( i , j , 0 ) f(i,j,0) f(i,j,0)的。(也就是锻造装备 i i i及其子树中的装备使用了 j j j个金币,全部作为价值贡献)
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 56, maxk = 105, maxm = 2e3 + 5, INF = 1e9 + 7;
char read_ch() {
char ch = getchar();
while (ch != 'A' && ch != 'B') ch = getchar();
return ch;
}
struct Edge {
int v, nex; LL w;
Edge(int v = 0, LL w = 0, int nex = 0) : v(v), w(w), nex(nex) {}
} E[maxn << 1];
int hd[maxn], tote;
void addedge(int u, int v, LL w) {
E[++tote] = Edge(v, w, hd[u]), hd[u] = tote;
}
int N, M, hsf[maxn];
LL cst[maxn], lim[maxn], v[maxn], f[maxn][maxk][maxm], g[maxm], ans;
void dfs(int u) {
if (!u) { //总的背包
for (int i = hd[u]; i; i = E[i].nex) {
dfs(E[i].v);
for (int j = M; j >= 0; j--)
for (int k = 0; k <= j; k++)
f[u][0][j] = max(f[u][0][j], f[u][0][j - k] + f[E[i].v][0][k]);
}
return ;
}
if (!hd[u]) { //叶子节点
lim[u] = min(lim[u], M / cst[u]);
for (int i = lim[u]; i >= 0; i--)
for (int j = i; j <= lim[u]; j++)
f[u][i][j * cst[u]] = (j - i) * v[u];
return ;
}
lim[u] = INF;
for (int i = hd[u]; i; i = E[i].nex) {
int v = E[i].v;
dfs(v);
lim[u] = min(lim[u], lim[v] / E[i].w), cst[u] += cst[v] * E[i].w;
}
lim[u] = min(lim[u], M / cst[u]);
for (int j = 0; j <= lim[u]; j++) {
for (int i = 0; i <= M; i++) g[i] = -INF;
g[0] = 0;
for (int i = hd[u]; i; i = E[i].nex) {
int v = E[i].v;
for (int x = M; x >= 0; x--) {
LL newgx = -INF;
for (int y = 0; y <= x; y++)
newgx = max(newgx, g[x - y] + f[v][j * E[i].w][y]);
g[x] = newgx;
}
}
for (int i = 0; i <= j; i++)
for (int k = 0; k <= M; k++)
f[u][i][k] = max(f[u][i][k], g[k] + (j - i) * v[u]);
}
}
int main() {
scanf("%d%d", &N, &M);
for (int i = 1; i <= N; i++) for (int j = 0; j <= 100; j++) for (int k = 0; k <= M; k++)
f[i][j][k] = -INF;
for (int i = 1; i <= N; i++) {
scanf("%lld", &v[i]); char t = read_ch();
if (t == 'A') {
int C; scanf("%d", &C);
while (C--) {
int v; LL w;
scanf("%d%lld", &v, &w);
addedge(i, v, w), hsf[v] = 1;
}
} else scanf("%lld%lld", &cst[i], &lim[i]);
}
for (int i = 1; i <= N; i++) if (!hsf[i]) addedge(0, i, 0);
dfs(0);
for (int i = 1; i <= M; i++) ans = max(ans, f[0][0][i]);
printf("%lld\n", ans);
return 0;
}