首先这个题很容易想到哈夫曼树,但哈夫曼是二进制编码的,而本题为k进制编码,因此也叫作k叉二叉树。既然想到这里,那类似二进制哈夫曼树的构造,我们先构造一个优先队列,(优先队列是个好东西,因为他可以自动排队啊,这里我们按小根堆,也就是队列中的元素从小到大排序),我们把前k个最小的数弹出来,组成一组,再把他们的和加入优先队列。现在就有一个问题,就是,你叶子那些层都填好了,填的满满的,但是,你最后一层,也就是根节点的下一层还有空,那这可就很浪费资源了,因为根节点的下一层的编码长度为1,你要是把一个叶子结点放了这个地方,那最后的编码长度一定更小啊。
所以这里就有一个很巧妙的方法,就是用0来顶,一直往优先队列里加0,直到根节点的下一层都填满。我们可以知道,一棵完整的哈夫曼树,只要不是叶子结点,每个节点都有k个孩子,那这棵哈夫曼树所有叶子结点总共有多少个呢?
以上图为例,此时k取3,要是2没有分支,那该树有3个叶子节点,但是,一旦有了分支,就会发现,多了两个,成了5个叶子结点了,所以,要想满哈夫曼树,那他的叶子结点个数一定为x*(k-1)+1。又因为我们已知有n个叶子结点,所以我们也就通过判断(n-1)%(k-1)来判断是不是满哈夫曼树,不满的话,用0去往上顶空,那需要顶多少个空呢,可以推算的要顶(k-1)-(n-1)%(k-1)个空。(很简单嘛,他不是不整除嘛,就是余数小于k-1,那把余数加到k-1不就行了嘛)。
好了,现在第一问结束,在一个问题是,那个层次,怎么算是最大。其实从队列里出来k个数相加后,算出来的那个数,就相当于在这k个数的基础上往上跳了一层,当然,这k个数层数可能不一样,那最后的和一定是最大那一层加一啊,所以我们只要求出最大一层再加一就行。
现在就上代码了
#include<bits/stdc++.h>
using namespace std;
struct node{
long long w,h;
node(){w=0;h=0;}
node(long long w,long long h):w(w),h(h){}
bool operator < (const node &a) const{return a.w==w?h>a.h:w>a.w;}
//优先队列默认大根堆,既堆顶元素最大,
//默认的bool operator() (const T& x, const T& y) const {return x<y;}
//要改成栈顶元素最小,需要重载比较内部函数<
};
priority_queue<node>q;
long long n,k,ans,sum=0,s1,a,layer=0,h;
int main(){
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a);
q.push(node(a,1));
sum+=a;
}
if(n<=k) {ans=sum;layer=1;}
else{
if((n-1)%(k-1)!=0)
{
for(int i=1;i<=((k-1)-(n-1)%(k-1));i++)
q.push(node(0,1));//非常重要,就是k叉二叉树最后一层可能不满,用0来代替
}
while(q.size()>=k){
s1=0;h=-1;
for(int i=1;i<=k;i++)
{
node x=q.top();q.pop();s1+=x.w;
h=max(h,x.h);
}
q.push(node(s1,h+1));
ans+=s1;
}
}
printf("%lld\n%lld",ans,q.top().h-1);
}