1 条题解

  • 0
    @ 2026-10-19 1:55:11

    [NOI 2015] 荷马史诗

    知识点

    哈夫曼树、优先队列、多目标贪心

    解题思路

    把k进制前缀码看作k叉树。补若干权重0的虚叶,使叶数减1能被k-1整除。每次取权重最小的k棵子树合并,权重相同时优先取高度较小者,维护总合并权重与高度。

    正确性说明

    前缀码对应叶子不为祖先的k叉树,带权路径长度等于各次合并权重之和。可在某个最优树中把最轻的k棵子树放为同一最深父节点的孩子,合并后缩小问题,得到Huffman贪心。相同权重交换不改变总长度,优先合并较矮子树避免把已有最长链继续加深,使最优总长度下的最大码长最小;补零叶使满k叉合并可行且不增加真实词的带权代价。

    易错点

    不能只按权重比较而忽略高度;每轮恰合并k个;补叶数取模,已整除时不补;权重和代价用64位。

    复杂度

    O(n log n)时间,O(n)空间,k≤9。

    参考代码(GNU C++17)

    #include<bits/stdc++.h>
    using namespace std;
    int main(){ios::sync_with_stdio(false);cin.tie(nullptr);int n,k;cin>>n>>k;using P=pair<long long,int>;priority_queue<P,vector<P>,greater<P>>q;for(int i=0;i<n;i++){long long w;cin>>w;q.push({w,0});}int add=(k-1-(n-1)%(k-1))%(k-1);while(add--)q.push({0,0});long long ans=0;while(q.size()>1){long long sum=0;int h=0;for(int i=0;i<k;i++){auto[w,d]=q.top();q.pop();sum+=w;h=max(h,d);}ans+=sum;q.push({sum,h+1});}cout<<ans<<'\n'<<q.top().second<<'\n';}
    
    • 1

    信息

    ID
    915
    时间
    1000ms
    内存
    125MiB
    难度
    提高
    标签
    递交数
    1
    已通过
    1
    上传者