Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
Don’t try to simulate all permutations. Stare at the item that gets added to the pile that becomes the final maximum. What was true about that pile immediately before this item was added?
If an item is placed last, it must be added to a minimum pile. So the value you can force looks like , where is the largest possible minimum load after arranging all the other items.
The greedy process before the last item is secretly just a partition problem: maximizing the final minimum load equals partitioning those items into groups while maximizing the minimum group sum.
The best last item is always a maximum element. If , replacing by inside any partition can reduce the bottleneck by at most , so choosing as the final item is never worse.
Remove one maximum element . Binary search : can the remaining items form at least disjoint groups each with sum ? Use mask DP storing . The answer is .
Take any optimal order. Let be the item whose addition creates the final maximum value . Right before adding , its pile has load . Since the rule always adds to a minimum pile, every pile has load at least , and
Now move every item after to before , and put at the very end. After the old prefix, the minimum load is already ; adding more positive items cannot make the minimum load go down. So before adding last, the minimum load is at least , and adding still achieves at least .
So we only need to consider solutions where the item that creates the answer is the last item. Good, the permutation monster just lost most of its teeth.
For a fixed last item , the best value is
where is the maximum possible minimum pile load after arranging all items of .
Claim:
One direction is obvious: any greedy run assigns items to piles, so it gives some partition. Its minimum load cannot beat the best partition bottleneck.
For the other direction, suppose we have a partition where every group has sum at least . We can realize minimum load at least by greedy:
Eventually all piles reach at least . Any leftover items can be placed afterward; the minimum never decreases. So the greedy problem and the partition bottleneck problem are exactly the same here.
Let , and let be all other items. Compare making last versus making last:
Let . Take an optimal partition for and replace by in its group. Only that group decreases, and it decreases by exactly , so
Therefore
So a larger last item is never worse. Remove one maximum element , solve the bottleneck partition problem on the remaining items, and add .
For a fixed value , we need to know whether the remaining items can form at least disjoint groups, each with sum at least .
Use bitmask DP over the remaining items. For each mask, store the best pair
where:
Transition by adding an unused item :
Closing a group immediately when it reaches is optimal for this DP: once a group is already good enough, dumping more items into it is usually just wasting ammunition.
If we can reach , then is feasible. Binary search the largest feasible .
There are only items after removing the maximum. One feasibility check costs
and binary search adds a factor of . Memory is
Given , this easily fits. The low time limit is annoying, not fatal. Classic Codeforces nonsense.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, k;
cin >> n >> k;
vector<ll> a(n);
for (ll &x : a) cin >> x;
int id = int(max_element(a.begin(), a.end()) - a.begin());
ll mx = a[id];
vector<ll> b;
b.reserve(n - 1);
ll total = 0;
for (int i = 0; i < n; i++) {
if (i == id) continue;
b.push_back(a[i]);
total += a[i];
}
int m = int(b.size());
int N = 1 << m;
vector<int> done(N);
vector<ll> rem(N);
auto can = [&](ll need) -> bool {
if (need == 0) return true;
fill(done.begin(), done.end(), -1);
done[0] = 0;
rem[0] = 0;
for (int mask = 0; mask < N; mask++) {
if (done[mask] < 0) continue;
int c = done[mask];
ll r = rem[mask];
for (int i = 0; i < m; i++) {
if (mask & (1 << i)) continue;
int nmask = mask | (1 << i);
int nc = c;
ll nr = r + b[i];
if (nr >= need) {
nc++;
nr = 0;
}
if (nc >= k) return true;
if (nc > done[nmask] || (nc == done[nmask] && nr > rem[nmask])) {
done[nmask] = nc;
rem[nmask] = nr;
}
}
}
return false;
};
ll lo = 0, hi = total / k;
while (lo < hi) {
ll mid = (lo + hi + 1) / 2;
if (can(mid)) lo = mid;
else hi = mid - 1;
}
cout << mx + lo << '\n';
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, k;
cin >> n >> k;
vector<ll> a(n);
for (ll &x : a) cin >> x;
int id = int(max_element(a.begin(), a.end()) - a.begin());
ll mx = a[id];
vector<ll> b;
b.reserve(n - 1);
ll total = 0;
for (int i = 0; i < n; i++) {
if (i == id) continue;
b.push_back(a[i]);
total += a[i];
}
int m = int(b.size());
int N = 1 << m;
vector<int> done(N);
vector<ll> rem(N);
auto can = [&](ll need) -> bool {
if (need == 0) return true;
fill(done.begin(), done.end(), -1);
done[0] = 0;
rem[0] = 0;
for (int mask = 0; mask < N; mask++) {
if (done[mask] < 0) continue;
int c = done[mask];
ll r = rem[mask];
for (int i = 0; i < m; i++) {
if (mask & (1 << i)) continue;
int nmask = mask | (1 << i);
int nc = c;
ll nr = r + b[i];
if (nr >= need) {
nc++;
nr = 0;
}
if (nc >= k) return true;
if (nc > done[nmask] || (nc == done[nmask] && nr > rem[nmask])) {
done[nmask] = nc;
rem[nmask] = nr;
}
}
}
return false;
};
ll lo = 0, hi = total / k;
while (lo < hi) {
ll mid = (lo + hi + 1) / 2;
if (can(mid)) lo = mid;
else hi = mid - 1;
}
cout << mx + lo << '\n';
}
return 0;
}