Game HDU - 5242 贪心 树链剖分

题解

题目大意 给你一个n个节点的树 有k个回合 每个回合从树根出发每个点只能经过一次 经过每个点时会获得得分 但是这个得分在整句游戏中只有第一次经过才可以获得 问k次最多能获得多少得分

利用树链剖分思想DFS在每个节点维护一个val值 表示走这个节点能获得的最大得分(重儿子val+自身值) 儿子中最大的val为重儿子
然后使用优先队列将根节点加入 每次选取最大val的节点出队并将重链上所有非重儿子节点加入队列 操作k次即可

AC代码

#include 
#include 
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;
}

你可能感兴趣的:(_数据结构_,贪心,树链剖分)