题目大意:
有
n
n
个单词,从~
N
N
编号,每个单词有个,我们用
k
k
进制串去表示第
i
i
个单词,对于任意的,都满足
si
s
i
不是
sj
s
j
的前缀,问如何选择
si
s
i
,能使替换后的总长度最小,在总长度最小的情况下,使得最长的
si
s
i
最小。
请回答最小总长度以及此时最长的
si
s
i
最小的情况。
2≤n≤100000,2≤k≤9 2 ≤ n ≤ 100000 , 2 ≤ k ≤ 9
分析:
我们如果将所有的
si
s
i
构成一颗
trie
t
r
i
e
树,可以发现,必定所有的单词的末尾都是在树的叶子节点,
因为任意一个单词都不能是另一个单词的前缀。
那么这题其实就是一颗哈夫曼树,
我们在求解的时候,因为要使最长的
si
s
i
最短,那么我们就在权值最小的情况下,优先考虑合并次数最少的(深度最小的)进行合并。
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<cmath>
using namespace std;
typedef long long ll;
struct Node
{
int h;
ll w;
Node(){};
Node(ll a, int b)
{
w = a, h = b;
}
friend bool operator < (Node aa, Node bb)
{
if (aa.w == bb.w) return aa.h > bb.h;
return aa.w > bb.w;
}
};
priority_queue <Node> Q;
int n, k;
int main()
{
scanf("%d %d", &n, &k);
ll x;
for (int i = 1; i <= n; i++)
{
scanf("%lld", &x);
Q.push(Node(x, 1));
}
if ((n - 1) % (k - 1) != 0)
for (int i = 1; i <= (k - 1) - (n - 1) % (k - 1); i++) Q.push(Node(0, 1));
ll ans = 0;
while (Q.size() >= k)
{
int Merge_cnt = - 1;
ll num = 0;
for (int i = 1; i <= k; i++)
{
num += Q.top().w;
Merge_cnt = max(Merge_cnt, Q.top().h);
Q.pop();
}
Q.push(Node(num, Merge_cnt + 1));
ans += num;
}
printf("%lld\n%d\n", ans, Q.top().h - 1);
return 0;
}