HDU 5242 Game 贪心
题意:
给出一颗有根树,每个顶点上有一个权值\(w_i\)。求前\(k\)大条链的权值之和。
分析:
首先\(dfs\)预处理一遍\(d(u)\),表示从顶点\(u\)往下走能得到的最大权值和,顺便还要记录这是从\(u\)的哪个儿子转移上来的。
最开始我们取树中最大的一条链,取完以后树会分裂成若干不相交的子树。然后再从这些子树中取一条能得到最大权值的链,然后继续分裂。
每次取走一条链后,新出现的子树的根节点是链上所有点的没被取走的儿子节点。
这个过程用一个优先队列维护即可。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#define MP make_pair
using namespace std;
typedef long long LL;
typedef pair<LL, int> PII;
const int maxn = 100000 + 10;
struct Edge
{
int v, nxt;
Edge() {}
Edge(int v, int nxt):v(v), nxt(nxt) {}
};
int root, ecnt, head[maxn];
Edge edges[maxn];
int n, k;
LL w[maxn];
int indeg[maxn];
void init() {
ecnt = 0;
memset(head, 0, sizeof(head));
memset(indeg, 0, sizeof(indeg));
}
void AddEdge(int u, int v) {
edges[++ecnt] = Edge(v, head[u]);
head[u] = ecnt;
}
LL d[maxn];
int Mson[maxn];
void dfs(int u) {
d[u] = w[u];
LL Max = -1;
for(int i = head[u]; i; i = edges[i].nxt) {
int v = edges[i].v;
dfs(v);
if(d[v] > Max) { Max = d[v]; Mson[u] = v; }
}
if(Max >= 0) d[u] += Max;
}
priority_queue<PII> Q;
int main()
{
int T; scanf("%d", &T);
for(int kase = 1; kase <= T; kase++) {
init();
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i++) scanf("%lld", w + i);
for(int i = 1; i < n; i++) {
int u, v; scanf("%d%d", &u, &v);
AddEdge(u, v);
indeg[v]++;
}
for(int i = 1; i <= n; i++) if(!indeg[i]) { root = i; break; }
memset(Mson, 0, sizeof(Mson));
dfs(root);
while(!Q.empty()) Q.pop();
Q.push(MP(d[root], root));
LL ans = 0;
for(int i = 0; i < k && !Q.empty(); i++) {
PII x = Q.top(); Q.pop();
ans += x.first;
int u = x.second;
for(; u; u = Mson[u]) {
for(int j = head[u]; j; j = edges[j].nxt) {
int v = edges[j].v;
if(v != Mson[u]) Q.push(MP(d[v], v));
}
}
}
printf("Case #%d: %lld\n", kase, ans);
}
return 0;
}