【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; }
This passage is made by MashiroSky.