[NOI2015]荷马史诗 题解

[NOI2015]荷马史诗 题解

题目地址:洛谷:【P2168】[NOI2015]荷马史诗 – 洛谷、BZOJ:Problem 4198. — [Noi2015]荷马史诗

题目描述

追逐影子的人,自己就是影子 ——荷马
Allison 最近迷上了文学。她喜欢在一个慵懒的午后,细细地品上一杯卡布奇诺,静静地阅读她爱不释手的《荷马史诗》。但是由《奥德赛》和《伊利亚特》 组成的鸿篇巨制《荷马史诗》实在是太长了,Allison 想通过一种编码方式使得它变得短一些。
一部《荷马史诗》中有n种不同的单词,从1到n进行编号。其中第i种单 词出现的总次数为wi。Allison 想要用k进制串si来替换第i种单词,使得其满足如下要求:
对于任意的 1 ≤ i, j ≤ n , i ≠ j ,都有:si不是sj的前缀。
现在 Allison 想要知道,如何选择si,才能使替换以后得到的新的《荷马史诗》长度最小。在确保总长度最小的情况下,Allison 还想知道最长的si的最短长度是多少?
一个字符串被称为k进制字符串,当且仅当它的每个字符是 0 到 k − 1 之间(包括 0 和 k − 1 )的整数。
字符串 str1 被称为字符串 str2 的前缀,当且仅当:存在 1 ≤ t ≤ m ,使得str1 = str2[1..t]。其中,m是字符串str2的长度,str2[1..t] 表示str2的前t个字符组成的字符串。

题意简述

一篇文章有$n$个单词,你想使用一种压缩算法压缩这篇文章,具体而言,用一个$k$进制数来代替一个单词,其中,任何两个单词的代表数不能一个是另一个前缀。

输入输出格式

输入格式:
输入的第 1 行包含 2 个正整数 n, k ,中间用单个空格隔开,表示共有 n种单词,需要使用k进制字符串进行替换。
接下来n行,第 i + 1 行包含 1 个非负整数wi ,表示第 i 种单词的出现次数。

输出格式:
输出包括 2 行。
第 1 行输出 1 个整数,为《荷马史诗》经过重新编码以后的最短长度。
第 2 行输出 1 个整数,为保证最短总长度的情况下,最长字符串 si 的最短长度。

输入输出样例

输入样例#1:

4 2
1
1
2
2

输出样例#1:

12
2

输入样例#2:

6 3
1
1
3
3
9
9

输出样例#2:

36
3

说明

【样例说明 1】
用 X(k) 表示 X 是以 k 进制表示的字符串。
一种最优方案:令 00(2) 替换第 1 种单词, 01(2) 替换第 2 种单词, 10(2) 替换第 3 种单词,11(2) 替换第 4 种单词。在这种方案下,编码以后的最短长度为:
1 × 2 + 1 × 2 + 2 × 2 + 2 × 2 = 12
最长字符串si的长度为 2 。
一种非最优方案:令 000(2) 替换第 1 种单词,001(2) 替换第 2 种单词,01(2)替换第 3 种单词,1(2) 替换第 4 种单词。在这种方案下,编码以后的最短长度为:
1 × 3 + 1 × 3 + 2 × 2 + 2 × 1 = 12
最长字符串 si 的长度为 3 。与最优方案相比,文章的长度相同,但是最长字符串的长度更长一些。
【样例说明 2】
一种最优方案:令 000(3) 替换第 1 种单词,001(3) 替换第 2 种单词,01(3) 替换第 3 种单词, 02(3) 替换第 4 种单词, 1(3) 替换第 5 种单词, 2(3) 替换第 6 种单词。
对于所有数据,保证 2≤n≤100000,2≤k≤9。
【提示】
选手请注意使用 64 位整数进行输入输出、存储和计算。
【时限1s,内存512M】

题解

$k=2$的时候,就是哈夫曼树的裸题了。现在我们重新考虑哈夫曼树的构建,是从单词中选出两个合并成一个点,作为一个新的点放进原集合中,直至合并为只有1个点,合并的过程其实就是把这些点都接到一个根上,从而形成一棵有根哈夫曼树。本题中,我们可以选$k$个点合并,每个点记录下出现次数及当前的替换字符串长,合并的时候新点的出现次数是原来$k$个点的出现次数之和,串长是原来$k$个点串长最大值+1。如此合并至只剩1个点即可,在合并的过程中维护答案。
但如果单词数量无法刚好合并成一棵满哈夫曼树怎么办?我们考虑用空点补全,这样就不需要特殊处理树不满的情况了。补到$n \bmod (k-1) = 1$即可,因为每次从集合中删$k-1$个点。
复杂度$O(n \log n)$。

代码

// Code by KSkun, 2018/7
#include <cstdio>
#include <cctype>
#include <cstring>

#include <algorithm>
#include <vector>
#include <queue>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2)
        ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1; register char c = fgc();
    for(; !isdigit(c); c = fgc()) if(c == '-') neg = -1;
    for(; isdigit(c); c = fgc()) res = (res << 1) + (res << 3) + c - '0';
    return res * neg;
}

const int MAXN = 100005;

int n, k;

typedef std::pair<LL, LL> PII64;
std::priority_queue<PII64, std::vector<PII64>, std::greater<PII64> > pq;

int main() {
    n = readint(); k = readint();
    for(int i = 1; i <= n; i++) {
        LL w = readint();
        pq.push(PII64(w, 0));
    }
    while(k > 2 && n % (k - 1) != 1) {
        pq.push(PII64(0, 0)); n++;
    }
    LL ans1 = 0, ans2 = 0;
    while(pq.size() > 1) {
        PII64 res(0, 0);
        for(int i = 1; i <= k; i++) {
            res.first += pq.top().first;
            res.second = std::max(res.second, pq.top().second + 1);
            pq.pop();
        }
        ans1 += res.first;
        ans2 = std::max(ans2, res.second);
        pq.push(res);
    }
    printf("%lld\n%lld", ans1, ans2);
    return 0;
}


发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据