貪心+哈夫曼樹--bzoj4198[NOI2015]荷馬史詩
阿新 • • 發佈:2019-02-11
看到這個題一臉懵逼啊?難道是字串??
結果一看題解,是什麼哈夫曼樹(不會啊)
這裡留個坑···還沒有十分了解哈夫曼樹是啥
但是看樣子好像就是一棵二叉樹,每次將權值小的合併成新的點
新點權值是原來的和
這個背景好像很裸???
Huffman樹之所以可以保證合法性,是因為加入了許多虛擬結點,保證了一定不會有一個是另一個字首的情況,考慮一個結點到根結點的路徑上的所有點一定都是虛擬結點,所以很容易確定構造方法的合法。
求第二問的時候我們可以合併的時候優先合併深度較小的
這樣就能保證整個樹的最大深度最小了(為什麼yy一下!)
當k≠2時,完美合併(感性理解這個詞)需要滿足n≡1 (mod k−1),不滿足這個條件時相當於額外添了幾個出現次數為0的單詞
所以k≠2時我們先補權值為0的節點使得可以完美合併,之後貪心即可
#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#define maxn 100005
#define LL long long
using namespace std;
int n,k,ans2;
LL ans1;
inline LL rd(){
LL x=0,f=1;char c=' ';
while(c<'0' || c>'9') {if(c=='-')f=-1;c=getchar();}
while(c<='9' && c>='0' ) x=x*10+c-'0',c=getchar();
return x*f;
}
struct node{
LL val; int dep;
inline bool operator <(const node &x) const {
return val>x.val||(val==x.val&&dep>x.dep);
}
}tmp;
priority_queue<node> q;
int main(){
scanf("%d%d",&n,&k); LL x;
for (int i=1;i<=n;i++){
x=rd(); q.push((node){x,1});
}
int rm=(n-1)%(k-1);
if(rm) rm=k-1-rm,n+=rm;
for(int i=1;i<=rm;i++) q.push((node){0,1});//用0來補齊
while(n>1){
int maxd=0; LL tot=0;
for(int i=1;i<=k;i++){
tmp=q.top(); q.pop();
tot+=tmp.val;
maxd=max(maxd,tmp.dep);
}
q.push((node){tot,maxd+1});//從下往上建樹
ans1+=tot; ans2=max(ans2,maxd);
n-=k-1;
}
printf("%lld\n%d\n",ans1,ans2);
}