【bzoj4198】 Noi2015—荷马史诗

http://www.lydsy.com/JudgeOnline/problem.php?id=4198 (题目链接)

题意

  一篇文章n个单词,每个出现了${w_i}$次,用k进制数代替单词,使得任意单词不是另一个单词的前缀。如何选择使文章的总长度最小,且在总长度最小情况下最长的k进制数的长度最小是多少

Solution

  LCF不写博客→_→,我只好自己写了。。

  将每个单词看成权值为${w_i}$的节点,很显然是个k叉哈弗曼树,考虑k=2的情况,就是一个“合并果子”。但是对于一般情况如果我们用用合并果子的做法会Wa,因为可能第一层的节点并没有满,那么如果把某一个叶子节点放到第一层一定会更优。所以我们加节点,其权值为0,直到${(k-1)|(n-1)}$,也就是说合并到最后一步一定正好剩下n个节点。

  考虑第二问,我们在堆中添加第二关键字,就是当前最深节点的深度。于是问题就解决了。

细节

  开LL,堆里面也要开→_→

代码

// bzoj4198
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define LL long long
#define inf 1<<30
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;

const int maxn=100010;
int n,K;

struct data {
	LL w;int d;
	friend bool operator < (const data a,const data b) {
		return a.w==b.w ? a.d>b.d : a.w>b.w;
	}
};
int main() {
	scanf("%d%d",&n,&K);
	priority_queue<data> q;
	LL ans=0;
	for (int i=1;i<=n;i++) {
		LL x;scanf("%lld",&x);
		q.push((data){x,0});
	}
	while ((n-1)%(K-1)) n++,q.push((data){0,0});
	while (q.size()>1) {
		LL w=0;int d=0;
		for (int i=1;i<=K;i++) {w+=q.top().w,d=max(d,q.top().d);q.pop();}
		ans+=w;
		q.push((data){w,d+1});
	}
	printf("%lld\n%d",ans,q.top().d);
    return 0;
}

 

posted @ 2017-01-12 16:28  MashiroSky  阅读(...)  评论(...编辑  收藏