解法:首先很显然是二分k的值,但是如果直接用堆来操作,时间复杂度会达到 Nlog(N)log(N) 的级别,而题目恶心之处就在于它把那个两个log的卡了,会T。所以考虑用另一个容器:链表。即对于每次前k个最小值合起来后插入到链表之中,因为插入的位置只会一直往后,所以用一个指针就好了,于是这样时间复杂度就降到了 Nlog(N)了,这样就不会T,但是还有一个细节,就是每次都找前k小的,如果不能刚好完全匹配呢?这样明显不是最优的状态了,所以我们还要加一个判断,即如果不能完全配好,那么我们当然希望第一次合并的数量最少(因为对后面的影响值最大),所以要计算一下第一次合并多少个,后面就全部找前k小的就好了。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
const long long maxn = 5000000;
long long a[maxn], nextt[maxn], tot, q[maxn], head, tail;
long long n, T, sum, len, p, l, r, mid, ans, rest, temp;
bool flag;
bool check(long long k) {
p = head;
flag = true;
sum = 0;
int step = 0;
while (flag) {
len = 0;
step++;
if (step == 1) {
temp = rest%(k-1);
if (temp == 0) temp = k-1;
else if (temp == 1) temp = k;
} else temp = k;
for (int i = 1; i <= temp; i++) {
len += a[head];
if (len > T) return false;
if (p == head) p = nextt[p];
head = nextt[head];
if (head == 0) {
flag = false;
break;
}
}
sum += len;
if (sum > T) return false;
if (!flag) break;
rest = rest-k+1;
if (len < a[head]) {
tot++;
a[tot] = len;
nextt[tot] = head;
head = tot;
p = tot;
continue;
}
while (p != tail && a[nextt[p]] < len) p = nextt[p];
tot++;
nextt[tot] = nextt[p];
a[tot] = len;
nextt[p] = tot;
if (p == tail) tail = nextt[tail];
}
return true;
}
int main() {
//freopen("g.in","r",stdin);
int tt;
scanf("%d", &tt);
while (tt--) {
scanf("%lld %lld", &n, &T);
for (int i = 1; i <= n; i++) scanf("%lld", &q[i]);
sort(q+1, q+1+n);
l = 2; r = n; ans = n;
while (l <= r) {
mid = (l+r)/2;
for (int i = 1; i <= n; i++) {
a[i] = q[i];
nextt[i] = i+1;
}
head = 1; tail = n;
nextt[tail] = 0;
tot = n;
rest = n;
if (check(mid)) {
ans = mid;
r = mid-1;
} else l = mid+1;
}
printf("%lld\n", ans);
}
}