[洛谷P1552][APIO2012]派遣

题目大意:有一棵$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;
}

  

原文地址:https://www.cnblogs.com/Memory-of-winter/p/10028302.html