题目大意:
给你n个长度为ai的序列,让你把这n个序列合并,每次合并需要一个cost,值为合并的序列的长度之和,每次最多可以合并k个序列。现在问你,给出cost最大为T,最小的k是多少。
解题思路:
显然,可以用优先队列水一发,复杂度为nlognlogn
二分枚举k,然后每次取最小的k个加在一起,加入队列,然后判断,加和是否比T大。
其实就是一个k维哈夫曼树的优先队列优化,来篇博客,传送门
代码:
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <functional>
using namespace std;
typedef long long LL;
const int maxn = 100005;
int a[maxn];
int n, t, cas;
int Scan() {
int res = 0, ch, flag = 0;
if ((ch = getchar()) == '-')
flag = 1;
else if (ch >= '0'&&ch <= '9')
res = ch - '0';
while ((ch = getchar()) >= '0'&&ch <= '9')
res = res * 10 + ch - '0';
return flag ? -res : res;
}
void Out(int a) {
if (a > 9)
Out(a / 10);
putchar(a % 10 + '0');
}
bool judge(int k) {
LL sum = 0, pre = 0;
int inx = 0, cnt = 0;
priority_queue<LL, vector<LL>, greater<LL> > q;
if ((n - 1) % (k - 1)) {
for (int i = 0; i < ((k - 1) - (n - 1) % (k - 1)); ++i) q.push(0);
}
while (inx < n) {
if (cnt < k) {
if (!q.empty() && q.top() < a[inx]) {
pre += q.top(); q.pop();
++cnt;
} else {
pre += a[inx++];
++cnt;
}
} else {
cnt = 0;
if (sum + pre > t) return false;
sum += pre;
q.push(pre); pre = 0;
}
}
while (!q.empty()) {
if (cnt < k) {
pre += q.top(); q.pop();
++cnt;
} else {
cnt = 0;
sum += pre;
if (sum > t) return false;
q.push(pre); pre = 0;
}
}
if (sum + pre > t) return false;
return true;
}
int main() {
cas = Scan();
while (cas--) {
n = Scan(); t = Scan();
for (int i = 0; i < n; ++i)
a[i] = Scan();
sort(a, a + n);
int l = 0, r = n, mid;
while (l < r) {
mid = (l + r) >> 1;
if (judge(mid)) r = mid;
else l = mid + 1;
}
Out(r); puts("");
}
return 0;
}