Solution [NOI2015]荷马史诗
题目大意:构造(k)叉哈夫曼树
哈夫曼树
分析:这题比较模板,可以当做复习初赛(大雾)
哈夫曼树的定义:
构造一棵有(n)个节点的(k)叉树,每个节点带有权(w_i),深度(d_i)(规定根深度为(0)),使得(sum w_i imes d_i)最小
当(k=2)时即为合并果子,因此可以想出显而易见的贪心算法,每次从堆中取(k)个合并,但是这样可能根节点的儿子不足(k)个,此时将叶节点移为根节点儿子会使得答案更优
因此我们需要额外补足一些(w=0)的虚拟点,让儿子不足的情况发生在叶子结点
每次将(k)个节点合并使得总个数减少(k-1)个,使得(n)个节点合并为(1)个减少了(n-1)个,因此当((k-1) | (n-1))时上述贪心算法正确
题目还要求代价和最小时最大深度尽量小,权值相同时按照深度排序即可
#include <cstdio>
#include <cctype>
#include <queue>
using namespace std;
typedef long long ll;
inline ll read(){
ll x = 0;char c = getchar();
while(!isdigit(c))c = getchar();
while(isdigit(c))x = x * 10 + c - '0',c = getchar();
return x;
}
struct HeapNode{
ll val;
int dep;
bool operator < (const HeapNode &rhs)const{
return (val == rhs.val) ? (dep > rhs.dep) : val > rhs.val;
}
};
priority_queue<HeapNode> Q;
int n,k;
ll ans1,ans2;
int main(){
n = read(),k = read();
for(int i = 1;i <= n;i++)
Q.push(HeapNode{read(),0});
while((n - 1) % (k - 1))Q.push(HeapNode{0,0}),n++;
while(Q.size() != 1){
ll add1 = 0,add2 = 0;
for(int i = 1;i <= k;i++){
HeapNode h = Q.top();Q.pop();
add1 += h.val;
add2 = max(add2,(ll)h.dep);
}
ans1 += add1;
ans2 = max(ans2,add2 + 1);
Q.push(HeapNode{add1,add2 + 1});
}
printf("%lld
%lld
",ans1,ans2);
return 0;
}