题目大意:有一棵$n$个点的树,和一个费用$m$,每个点有一个费用和价值,请选一个点,再从它的子树中选取若干个点,使得那个点的价值乘上选的点的个数最大,要求选的点费用总和小于等于$m$
题解:树形$dp$,贪心可得选的点一定是费用最少的几个点,可以用可并堆,大根堆,若总费用大于$m$就把堆顶弹掉,直到小于等于$m$,更新答案
卡点:无
C++ Code:
#include <algorithm>
#include <cstdio>
#define maxn 100010
int head[maxn], cnt;
struct Edge {
int to, nxt;
} e[maxn];
inline void addedge(int a, int b) {
e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt;
}
long long ans;
int V[maxn], lc[maxn], rc[maxn], dis[maxn];
int sum[maxn], sz[maxn];
inline int update(int x) {
sz[x] = sz[lc[x]] + sz[rc[x]] + 1;
sum[x] = sum[lc[x]] + sum[rc[x]] + V[x];
return x;
}
int merge(int x, int y) {
if (!x || !y) return x | y;
if (V[x] < V[y]) std::swap(x, y);
rc[x] = merge(rc[x], y);
if (dis[rc[x]] > dis[lc[x]]) std::swap(lc[x], rc[x]);
dis[x] = dis[rc[x]] + 1;
return update(x);
}
inline int pop(int x) {return merge(lc[x], rc[x]);}
int root, n, m;
int L[maxn];
int dfs(int u, int fa = 0) {
int rt = u;
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to, __rt = dfs(v);
rt = merge(rt, __rt);
while (sum[rt] > m) rt = pop(rt);
}
ans = std::max(ans, static_cast<long long> (sz[rt]) * L[u]);
return rt;
}
int main() {
scanf("%d%d", &n, &m); dis[0] = -1;
for (int i = 1, x; i <= n; i++) {
scanf("%d%d%d", &x, V + i, L + i);
sum[i] = V[i], sz[i] = 1;
if (x) addedge(x, i);
else root = i;
}
dfs(root);
printf("%lld\n", ans);
return 0;
}