[NOI2015]荷马史诗

题意

  给定 n 个单词的出现次数,将这n个单词用 n k进制字符串代替这 n 个单词,要求任意一个字符串不是另一个字符串的前缀
  求出一种方案使得替换后的总长度最小,在总长度最小的前提下,尽量使最长字符串的长度变小

题解

  如果是以前学过哈夫曼树的人,应该能够一眼看出这就是k进制哈夫曼树
  因为当 (n1)%(k1)!=0 时,哈夫曼树会不满,所以我们加入 k1(n1)%(k1) 个权为0的点以方便解题
  使用哈夫曼树能够轻易的满足第一个条件:总长度最小。为了使第二个条件也满足,我们考虑将一个点变成一个二元组:(点权,深度)(所有点初始深度为0),先按点权从小到大,点券相同就按深度从小到大
  然后使用优先队列存点,在合并时,新节点的深度就是选出的k个点的深度的最大值+1,然后将新节点丢入队列中,如此反复,知道队列中只剩下一个元素时停止
  第二问的答案很好求,最长字符串的长度就是最后剩下的点的深度;考虑怎么统计总长度:在合并的过程中(一开始将初始点兑入队列中不算),每往队列中丢入一个元素,ans+=sum(sum为该元素的点权),这样的话,第 i 层的点的点权会被加上i次,就相当于是出现次数*字符串长度,因此这样得到的答案就是总长度

复杂度

O( nlogn )

代码

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<queue>
#define Rint register int
#define Lint long long int
using namespace std;
const int N=100010;
struct node
{
    Lint val;
    int dep;
    bool operator < (const node &a) const
    {
        return val==a.val ? dep>a.dep:val>a.val;
    }
};
Lint w[N],ans;
int n,k,l;
priority_queue<node> q;
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)   scanf("%lld",&w[i]);
    sort( w+1,w+n+1 );
    for(int i=1;i<=n;i++)   q.push( (node){ w[i],0 } );
    while( (n-1)%(k-1) )   q.push( (node){ 0,0 } ),n++;
    while( q.size()>1 )
    {
        Lint sum=0;
        int num=1,mx=0;
        while( num<=k )
        {
            sum+=q.top().val;
            num++,mx=max( mx,q.top().dep );
            q.pop();
        }
        ans+=sum;
        q.push( (node){ sum,mx+1 } );
    }
    printf("%lld\n",ans);
    printf("%d\n",q.top().dep);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值