题解
题目大意 给你一个n个节点的树 有k个回合 每个回合从树根出发每个点只能经过一次 经过每个点时会获得得分 但是这个得分在整句游戏中只有第一次经过才可以获得 问k次最多能获得多少得分
利用树链剖分思想DFS在每个节点维护一个val值 表示走这个节点能获得的最大得分(重儿子val+自身值) 儿子中最大的val为重儿子
然后使用优先队列将根节点加入 每次选取最大val的节点出队并将重链上所有非重儿子节点加入队列 操作k次即可
AC代码
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e5 + 10;
ll val[MAXN]; //子链最大值+自身值
vector<int> edg[MAXN];
int son[MAXN]; //重儿子
struct node
{
int x;
ll v;
bool operator < (const node &oth) const
{
return v < oth.v;
}
};
ll DFS(int x) //计算每个节点val并标记重儿子
{
ll mx = 0;
for (int i = 0; i < edg[x].size(); i++)
{
ll res = DFS(edg[x][i]);
if (!son[x] || res > mx)
mx = res, son[x] = edg[x][i];
}
return val[x] += mx;
}
ll BFS(int k) //从根节点出发 贪心
{
priority_queue<node> pq;
pq.push({ 1, val[1] });
ll res = 0;
for (int i = 0; i < k && pq.size(); i++)
{
res += pq.top().v; //将最大值加入
int p = pq.top().x;
pq.pop();
while (edg[p].size()) //将路径上所有次大值节点入队
{
for (int i = 0; i < edg[p].size(); i++)
{
int q = edg[p][i];
if (q != son[p])
pq.push({ q, val[q] });
}
p = son[p]; //转移到重儿子继续
}
}
return res;
}
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
int T, kase = 0;
cin >> T;
while (T--)
{
int N, K;
cin >> N >> K;
for (int i = 1; i <= N; i++)
scanf("%lld", &val[i]), edg[i].clear();
for (int i = 1; i < N; i++)
{
int a, b;
scanf("%d%d", &a, &b);
edg[a].push_back(b);
}
DFS(1);
ll res = BFS(K);
printf("Case #%d: %lld\n", ++kase, res);
}
return 0;
}