BZOJ4198: [Noi2015]荷马史诗 哈夫曼树

本文介绍了一种实现K叉哈夫曼树的方法,并通过贪心算法来构造树,确保最大深度最小。代码中使用自定义堆结构来优化节点合并过程,避免使用标准库中的priority_queue。

题目大意:k叉哈夫曼树,且要求最大深度最小
对于第一条,先将多余的部分也就是n%(k-1)个预先合并,注意如果n%(k-1)==1就不要合并了,n%(k-1)==0要合并k-1个。
最大深度最小,直接贪心即可。
std::priority_queue慢得吓人

#include<cstdio>
#include<queue>
#define gm 100001
using namespace std;
typedef unsigned long long ll;
inline ll max(const ll &a,const ll &b)
{
    return a<b?b:a;
}
struct node
{
    node* f;
    ll w;
    ll d;
    node():f(0),w(0),d(0){}
    inline void son(node* const &x)
    {
        x->f=this;
        w+=x->w;
        d=max(d,x->d+1);
    }
}word[gm],*now;
typedef node* elec;
#define cmp(a,b) (a->w<b->w || a->w==b->w&&a->d<b->d)
struct heap
{
    typedef unsigned int u;
    elec h[gm];
    u n;
    heap(u n):n(n)
    {
        for(int i=1;i<=n;i++)
        h[i]=word+i;
        for(int i=n>>1;i>=1;i--)
        down(i);
    }
    inline void down(u x)
    {
        static u j;
        static elec v;
        v=h[x];
        while((x<<1)<=n)
        {
            j=x<<1;
            if(j+1<=n&&cmp(h[j+1],h[j]))
            j++;
            if(cmp(h[j],v))
            {
                h[x]=h[j];
                x=j;
            }
            else break;
        }
        h[x]=v;
    }
    inline elec top()
    {
        return h[1];
    }
    inline void pop()
    {
        h[1]=h[n--];
        down(1);
    }
    inline void push(const elec &v)
    {
        static u x;
        x=++n;
        while(x>1)
        {
            if(cmp(v,h[x>>1]))
            {
                h[x]=h[x>>1];
                x>>=1;
            }
            else break;
        }
        h[x]=v;
    }
};
int n,k;
ll getdpt(elec x)
{
    ll res=0;
    while(x->f)
    ++res,x=x->f;
    return res;
}
int main()
{
    //freopen("epic.in","r",stdin);
    //freopen("epic.out","w",stdout);
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    scanf("%llu",&word[i].w);
    heap& h=*(new heap(n));
    int rem=n%(k-1);
    if(!rem) rem=k-1;
    if(rem!=1)
    {
    now=new node;
    for(int i=1;i<=rem;i++)
    {
        now->son(h.top());
        h.pop();
    }
    h.push(now);
    }
    rem=n-rem+1;
    for(;rem!=1;rem-=k-1)
    {
        now=new node;
        for(int j=1;j<=k;j++)
        {
            now->son(h.top());
            h.pop();
        }
        h.push(now);
    }
    ll minlen=0;
    ll maxlen=0;
    ll kre;
    for(int i=1;i<=n;i++)
    {
        kre=getdpt(word+i);
        minlen+=kre*word[i].w;
        maxlen=max(maxlen,kre);
    }
    printf("%llu\n%llu\n",minlen,maxlen);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值