题目大意:有一棵$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 ", ans); return 0; }