【NOI2015】荷马史诗

显然一个Huffman树,然后改成 (K) 叉的就好。但是我不会Huffman树,于是学学拓宽知识面。

首先这个 (K) 叉树就是一个类似字典树的东西,但是不对应目标串的点都是非叶结点(不然不满足前缀关系的限制)

容易证明,每个点带 (K - 1) 个叶子是最优的。

考虑计算总权值时的 深度乘以权值 改为 它到根的链上都加上它的权值,那么 (sum dep_i imes val_i = sum_{u otin leaves} v_i)

于是让权值大的尽量贡献最少,那么就要出现在深度浅的地方。

因此考虑自底向上构建。考虑维护一个堆,每次取出前 (K) 小的,合并成一个点,然后扔进堆里。合并的时候维护权值(子节点的和)。直到合并成单个点。显然,权值越大的贡献次数越小。于是这样贪心是正确的(然而我并不会严格的证明)。

然后就建出一个 Huffman树了。但是对于第二问我们还是要得出深度最小值,于是加入第二关键字深度,合并的时候注意维护就行了。

还有因为每次减少 (K - 1) 个,所以可能最后剩下不是 (1) 个,所以,补上几个 (0) 使得 (n equiv 1 (mod K - 1)),显然不会对答案产生影响。

我挂在了补 (0) 以及 第二关键字上,WA了两发。

#include <bits/stdc++.h>

typedef long long LL;
typedef std::pair<LL, int> P;
std::priority_queue<P, std::vector<P>, std::greater<P> > q;
int n, K; LL t;
int main() {
	std::ios_base::sync_with_stdio(false), std::cin.tie(0);
	std::cin >> n >> K;
	for (int i = 1; i <= n; ++i) std::cin >> t, q.emplace(t, 0);
	while (n % (K - 1) != 1 % (K - 1)) ++n, q.emplace(0, 0);
	LL ans = 0;
	while (q.size() != 1) {
		LL tv = 0; int dx = 0;
		for (int i = 1; i <= K; ++i)
			tv += q.top().first, dx = std::max(dx, q.top().second), q.pop();
		q.emplace(tv, dx + 1); ans += tv;
	}
	std::cout << ans << std::endl << q.top().second << std::endl;
	return 0;
}
原文地址:https://www.cnblogs.com/daklqw/p/11587281.html