【NOI2015】荷马史诗

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

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

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

考虑计算总权值时的 深度乘以权值 改为 它到根的链上都加上它的权值,那么 \(\sum dep_i \times val_i = \sum_{u \notin 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值