Codeforces Round #763 (Div. 2) C - Balanced Stone Heaps
题意:
给定\(n\)堆石子,然后第\(i\)堆石子的数量为\(h_i\),现在从第\(3\)堆石子到第\(n\)堆石子依次的进行如下操作:选择一个整数\(d(0\le3\times d\le h_i)\),分配给第\(i - 1\)堆\(d\)个石子,分配给第\(i - 2\)堆\(d\times2\)个石子,问最后这堆石子中的最小值最大可以为多少?
- 一眼二分
- 注意题目中需要按照从前往后的顺序搬运石子,这就意味着后面补充的石子不能再用来补充前面的空,也就是某个位置能搬向前面位置的石子最多就是\(h_i\)个
- 注意到这点后,我们直接从后往前贪心的让每个位置恰好\(\ge\)二分枚举的值即可,有上述第二点保证了从后向前的合法性
- \(O(nlogn)\)
#include <bits/stdc++.h>
using namespace std;
//特殊在 题目要求操作的顺序是从前往后 但是我们从后面转移计算
//此时 需要注意 当前石子后面转移过来的石子不能再用了 因为从前往后注定了后面的石子只能往前搬一次
void solve() {
int n,limit = 0; cin >> n;
vector<int>h(n + 1);
for(int i = 1;i <= n;i ++) {
cin >> h[i];
limit = max(limit,h[i]);
}
auto check = [&](int x) {
vector<int>T(n + 1);
for(int i = 1;i <= n;i ++) T[i] = h[i];
for(int i = n;i >= 3;i --) {
if(T[i] < x) return false;
int d = min(T[i] - x,h[i]) / 3;
//T[i]此时代表的是后面的石子能补充过来的最大值 它可以帮助我们达到合法答案 但是不能用于向前搬运 所以要加个min
T[i - 1] += d;
T[i - 2] += d * 2;
}
if(T[1] < x || T[2] < x) return false;
return true;
};
int l = 1,r = limit,ans;
while(l <= r) {
int mid = l + r >> 1;
if(check(mid)) {
// cout << l << ' ' << r << ' ' << mid << '\n';
ans = mid,l = mid + 1;
} else {
r = mid - 1;
}
}
cout << ans << '\n';
}
int main() {
int T; cin >> T;
while(T --) {
solve();
}
return 0;
}