并不对劲的bzoj4198:loj2132:uoj130:p2168:[NOI2015]荷马史诗

题目大意

\(n\)(\(n\leq10^5\))种单词,其中第\(i\)种单词在文章中的出现次数为\(w_i\)
要将每个单词替换成一个字符集为\(k\)(\(k\leq9\))的字符串,使对于任意两个单词,其中一个的字符串不是另一个的前缀

题解

哈夫曼树贪心,每次合并\(k\)
需要注意的是,有时候第一层填不满。
除了连接叶子的分支节点以外,如果有一个点的儿子数目不是0或k,就一定不是最优解(可以把子树中的一个叶子拿上来使答案更优)。
但是按照哈夫曼树的贪心方式,最连接叶子节点的分支节点最先安排,无法确定每个这类分别安排多少个儿子。
会发现添加若干个出现次数为0的单词并不会影响答案。
可以添加一些出现次数为0的单词,使刚好补满叶子节点的分支节点的儿子。

代码
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#define rep(i,x,y) for(register int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(register int i=(x);i>=(y);--i)
#define maxn 100010
#define maxm 200010
#define int long long
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)&&ch!='-')ch=getchar();
    if(ch=='-')f=-1,ch=getchar();
    while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    return x*f;
}
void write(int x)
{
    if(x==0){putchar('0'),putchar('\n');return;}
    int f=0;char ch[20];
    if(x<0)putchar('-'),x=-x;
    while(x)ch[++f]=x%10+'0',x/=10;
    while(f)putchar(ch[f--]);
    putchar('\n');
    return;
}
int n,k,ans1,ans2;
priority_queue<pii >q;
signed main()
{
    n=read(),k=read();
    rep(i,1,n){int w=read();q.push(make_pair(-w,0));}
    if((n-1)%(k-1))
    {
        int lim=k-1-(n-1)%(k-1);
        rep(i,1,lim)q.push(make_pair(0,0));
    }
    while(q.size()>k)
    {
        int cnt=0,tmp1=0,tmp2=0;
        while((++cnt)<=k)
        {
            pii x=q.top();q.pop();
            tmp1+=-x.fi,tmp2=max(-x.se+1,tmp2);
        }
        ans1+=tmp1;q.push(make_pair(-tmp1,-tmp2));
    }
    while(!q.empty())
    {
        pii x=q.top();q.pop();
        ans1+=-x.fi,ans2=max(ans2,-x.se+1);
    }
    write(ans1),write(ans2);
    return 0;
}
posted @ 2019-02-18 07:57  echo6342  阅读(120)  评论(0编辑  收藏  举报