Bridging the Gap2
题目大意
共有 n ( 1 ≤ n ≤ 5 × 1 0 5 ) n(1 \le n \le 5 \times 10^5) n(1≤n≤5×105)个人处在河的一侧,现在有一艘船,船上只能容纳 k k k个人, k ∈ [ l , r ] k \in [l, r] k∈[l,r]。问能否将所有人送至河的另一侧。
解题思路
假设这 n n n个人一开始处在河的左侧,可以推出,从左侧划船至右侧在整个过程中是一定可行的。
因为,每次从右侧划回来都至少会带回 l l l个人,那么左侧人数一定始终大于 l l l,并且他们的体力也一定大于等于 1 1 1,要将所有人送至右侧,自然不会让划回左侧后没体力的人回来,所以从右侧回来的人体力一定大于等于 1 1 1,原本留在左侧的人体力也一定大于等于 1 1 1。
所以,我们只需要保证整个过程中,每次都至少有 l l l个人能从右侧划回左侧,并且他们的体力大于等于 2 2 2。
那么首先,我们算出整个过程中从右侧返回左侧的总次数, S = ⌈ n − r r − l ⌉ S = \lceil\frac{n - r}{r- l}\rceil S=⌈r−ln−r⌉,即第一次划向右侧会使左侧减少 r r r个人,接下来每个来回都会使左侧减少 r − l r - l r−l 个人,最后上取整即可。
接下来算出每个人最多能从右侧返回左侧多少次, a i = m i n ( ⌊ h i − 1 2 ⌋ , S ) a_i = min(\lfloor\frac{h_i - 1}{2}\rfloor, S) ai=min(⌊2hi−1⌋,S),即第一次划向右侧后,剩下的体力值能划几个来回,由于将所有人都送至对岸的次数为 S S S次,所以还要与 S S S取 m i n min min。
将所有人送至河的另一侧等价于每次都至少有 l l l个人能从右侧划回左侧,即 S l Sl Sl,最后只需要与 ∑ a i \sum a_i ∑ai做个比较即可。
因为 a i ≤ S a_i \le S ai≤S,所以每次划回来第 i i i个人只算了一次贡献,如果第 i i i个人体力耗尽,那就再找一个人假设为 j j j,来继续带他完成这 S S S次划船。
所以当 ∑ a i ≤ S l \sum a_i \le Sl ∑ai≤Sl,则可以将所有人送至河的另一侧,反之则不能。
参考代码
#include <bits/stdc++.h>
#define maxn 500100
#define int long long
using namespace std;
const double eps = 1e-8;
int a[maxn];
void solve() {
int n, l, r, sum = 0, cnt = 0;
cin >> n >> l >> r;
sum = (n - r + r - l - 1) / (r - l);
for (int i = 1; i <= n; ++i) {
cin >> a[i];
cnt += min((a[i] - 1) / 2, sum);
}
if(cnt >= sum * l) cout << "Yes\n";
else cout << "No\n";
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int t = 1;
while (t--) {
solve();
}
return 0;
}