[NOI2015] 荷马史诗
题目背景
追逐影子的人,自己就是影子 —— 荷马
题目描述
Allison 最近迷上了文学。她喜欢在一个慵懒的午后,细细地品上一杯卡布奇诺,静静地阅读她爱不释手的《荷马史诗》。但是由《奥德赛》和《伊利亚特》 组成的鸿篇巨制《荷马史诗》实在是太长了,Allison 想通过一种编码方式使得它变得短一些。
一部《荷马史诗》中有 n n n 种不同的单词,从 1 1 1 到 n n n 进行编号。其中第 i i i 种单词出现的总次数为 w i w_i wi。Allison 想要用 k k k 进制串 s i s_i si 来替换第 i i i 种单词,使得其满足如下要求:
对于任意的 1 ≤ i , j ≤ n 1\leq i, j\leq n 1≤i,j≤n , i ≠ j i\ne j i=j ,都有: s i s_i si 不是 s j s_j sj 的前缀。
现在 Allison 想要知道,如何选择 s i s_i si,才能使替换以后得到的新的《荷马史诗》长度最小。在确保总长度最小的情况下,Allison 还想知道最长的 s i s_i si 的最短长度是多少?
一个字符串被称为 k k k 进制字符串,当且仅当它的每个字符是 0 0 0 到 k − 1 k-1 k−1 之间(包括 0 0 0 和 k − 1 k-1 k−1 )的整数。
字符串 s t r 1 str1 str1 被称为字符串 s t r 2 str2 str2 的前缀,当且仅当:存在 1 ≤ t ≤ m 1 \leq t\leq m 1≤t≤m ,使得 s t r 1 = s t r 2 [ 1.. t ] str1 = str2[1..t] str1=str2[1..t]。其中, m m m 是字符串 s t r 2 str2 str2 的长度, s t r 2 [ 1.. t ] str2[1..t] str2[1..t] 表示 s t r 2 str2 str2 的前 t t t 个字符组成的字符串。
输入格式
输入的第 1 1 1 行包含 2 2 2 个正整数 n , k n, k n,k ,中间用单个空格隔开,表示共有 n n n 种单词,需要使用 k k k 进制字符串进行替换。
接下来 n n n 行,第 i + 1 i + 1 i+1 行包含 1 1 1 个非负整数 w i w_i wi,表示第 i i i 种单词的出现次数。
输出格式
输出包括 2 2 2 行。
第 1 1 1 行输出 1 1 1 个整数,为《荷马史诗》经过重新编码以后的最短长度。
第 2 2 2 行输出 1 1 1 个整数,为保证最短总长度的情况下,最长字符串 s i s_i si 的最短长度。
样例 #1
样例输入 #1
4 2
1
1
2
2
样例输出 #1
12
2
样例 #2
样例输入 #2
6 3
1
1
3
3
9
9
样例输出 #2
36
3
提示
【样例解释】
样例 1 解释
用 X ( k ) X(k) X(k) 表示 X X X 是以 k k k 进制表示的字符串。
一种最优方案:令 00 ( 2 ) 00(2) 00(2) 替换第 1 1 1 种单词, 01 ( 2 ) 01(2) 01(2) 替换第 2 种单词, 10 ( 2 ) 10(2) 10(2) 替换第 3 3 3 种单词, 11 ( 2 ) 11(2) 11(2) 替换第 4 4 4 种单词。在这种方案下,编码以后的最短长度为:
1 × 2 + 1 × 2 + 2 × 2 + 2 × 2 = 12 1 × 2 + 1 × 2 + 2 × 2 + 2 × 2 = 12 1×2+1×2+2×2+2×2=12
最长字符串 s i s_i si 的长度为 2 2 2 。
一种非最优方案:令 000 ( 2 ) 000(2) 000(2) 替换第 1 1 1 种单词, 001 ( 2 ) 001(2) 001(2) 替换第 2 2 2 种单词, 01 ( 2 ) 01(2) 01(2) 替换第 3 3 3 种单词, 1 ( 2 ) 1(2) 1(2) 替换第 4 4 4 种单词。在这种方案下,编码以后的最短长度为:
1 × 3 + 1 × 3 + 2 × 2 + 2 × 1 = 12 1 × 3 + 1 × 3 + 2 × 2 + 2 × 1 = 12 1×3+1×3+2×2+2×1=12
最长字符串 s i s_i si 的长度为 3 3 3 。与最优方案相比,文章的长度相同,但是最长字符串的长度更长一些。
样例 2 解释
一种最优方案:令 000 ( 3 ) 000(3) 000(3) 替换第 1 1 1 种单词, 001 ( 3 ) 001(3) 001(3) 替换第 2 2 2 种单词, 01 ( 3 ) 01(3) 01(3) 替换第 3 3 3 种单词, 02 ( 3 ) 02(3) 02(3) 替换第 4 4 4 种单词, 1 ( 3 ) 1(3) 1(3) 替换第 5 种单词, 2 ( 3 ) 2(3) 2(3) 替换第 6 6 6 种单词。
【数据规模与约定】
所有测试数据的范围和特点如下表所示(所有数据均满足 0 < w i ≤ 1 0 11 0 < w_i \leq 10^{11} 0<wi≤1011):
测试点编号 | n n n 的规模 | k k k 的规模 | 备注 |
---|---|---|---|
1 1 1 | n = 3 n=3 n=3 | k = 2 k=2 k=2 | |
2 2 2 | n = 5 n=5 n=5 | k = 2 k=2 k=2 | |
3 3 3 | n = 16 n=16 n=16 | k = 2 k=2 k=2 | 所有 w i w_i wi 均相等 |
4 4 4 | n = 1 000 n=1\,000 n=1000 | k = 2 k=2 k=2 | w i w_i wi 在取值范围内均匀随机 |
5 5 5 | n = 1 000 n=1\,000 n=1000 | k = 2 k=2 k=2 | |
6 6 6 | n = 100 000 n=100\,000 n=100000 | k = 2 k=2 k=2 | |
7 7 7 | n = 100 000 n=100\,000 n=100000 | k = 2 k=2 k=2 | 所有 w i w_i wi 均相等 |
8 8 8 | n = 100 000 n=100\,000 n=100000 | k = 2 k=2 k=2 | |
9 9 9 | n = 7 n=7 n=7 | k = 3 k=3 k=3 | |
10 10 10 | n = 16 n=16 n=16 | k = 3 k=3 k=3 | 所有 w i w_i wi 均相等 |
11 11 11 | n = 1 001 n=1\,001 n=1001 | k = 3 k=3 k=3 | 所有 w i w_i wi 均相等 |
12 12 12 | n = 99 999 n=99\,999 n=99999 | k = 4 k=4 k=4 | 所有 w i w_i wi 均相等 |
13 13 13 | n = 100 000 n=100\,000 n=100000 | k = 4 k=4 k=4 | |
14 14 14 | n = 100 000 n=100\,000 n=100000 | k = 4 k=4 k=4 | |
15 15 15 | n = 1 000 n=1\,000 n=1000 | k = 5 k=5 k=5 | |
16 16 16 | n = 100 000 n=100\,000 n=100000 | k = 7 k=7 k=7 | w i w_i wi 在取值范围内均匀随机 |
17 17 17 | n = 100 000 n=100\,000 n=100000 | k = 7 k=7 k=7 | |
18 18 18 | n = 100 000 n=100\,000 n=100000 | k = 8 k=8 k=8 | w i w_i wi 在取值范围内均匀随机 |
19 19 19 | n = 100 000 n=100\,000 n=100000 | k = 9 k=9 k=9 | |
20 20 20 | n = 100 000 n=100\,000 n=100000 | k = 9 k=9 k=9 |
【提示】
选手请注意使用 64 位整数进行输入输出、存储和计算。
【评分方式】
对于每个测试点:
- 若输出文件的第 1 1 1 行正确,得到该测试点 40 % 40\% 40% 的分数;
- 若输出文件完全正确,得到该测试点 100 % 100\% 100% 的分数。
思路
拓展:huffman树用于求深度和权重,tire树用于求公共前缀。
- 本道题首先是要最小化深度和最小权值和。
- 我们可以把各个单词的编码看成叶节点的权值,单词长度就可以看成叶节点的深度,那么,最短长度就是这棵树的WPL(就是合并果子的那种 k 个 k 个合并)。
- 很抽象是不,我们看张图:
- 注意:其中的 0 是我们自己补的。
- 最后注意一点,题目让我们的最长的长度最短,也就是说要让深度最短,所以每一次选取权值相同的树时,尽量选取深度短的来建
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#define int long long
using namespace std;
typedef pair<int,int>PII;//第一维度为权值,第二维度为高度
const int N = 1e5+10;
int n,k;
priority_queue<PII,vector<PII>,greater<PII>>heap;
int w[N];
signed main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>w[i];
heap.push({w[i],0});
}
//满足k叉堆的条件:n-m(k-1)=1
//也就是(n-1)%(k-1)=0
while((n-1)%(k-1)){
heap.push({0,0});
n++;
}
int res=0;
while(heap.size()>1){
int hight=0;//高度
int sum=0;
for(int i=0;i<k;i++){
sum+=heap.top().first;
hight=max(hight,heap.top().second);
heap.pop();
}
res+=sum;
heap.push({sum,hight+1});
}
cout<<res<<endl;
cout<<heap.top().second<<endl;
return 0;
}