题解 | #请客吃饭#

请客吃饭

https://www.nowcoder.com/practice/4250a369235e414ba9128bb23ff4fcf5

二分答案+滑动窗口

我们来看看为什么可以想出这个思路

首先我们假设所有的人都是数轴上的点,他们的坐标就是 a ,权值就是 b

对于一个隔阂值 m ,代表在数轴上选择一段长度为 m 的区间,区间里点的权值和就是贡献

那么这个区间就是滑动窗口,他的右端点一定在一个点上,因为从贪心的角度来看,如果右端点不在一个点上,那么我们可以把右端点向左移动,这样就可能会覆盖其他点,权值和就可能会增加

我们可以遍历维护出这个窗口,每次向右滑动到一个新的点,这时窗口中已经存在的点可能会离开窗口,但是一定是最左边的点离开,所以我们可以用一个双端队列来维护,同时记录最大的权值和,如果大于等于 k ,代表这个 m 是合法的

然后我们发现 m 是单调的,假设最终的答案是 M ,那么对于任意的 m >= M ,其权值和都是大于等于 k 的,也都是合法的,对于任意的 m < M ,其权值和都是小于 k 的,也都是不合法的,所以我们可以二分答案

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 5;
int __t = 1, n, k, a, b;
vector<pair<int, int>> v;
int ck(int m) {
    deque<pair<int, int>> q;
    int sum = 0, ma = 0;
    for (auto [a, b] : v) {
        q.push_back({a, b}), sum += b;
        while (!q.empty() && q.front().first < a - m)
            sum -= q.front().second, q.pop_front();
        ma = max(ma, sum);
    }
    return ma >= k;
}
void solve() {
    cin >> n >> k;
    v.resize(n);
    for (int i = 0; i < n; i++)
        cin >> v[i].first;
    for (int i = 0; i < n; i++)
        cin >> v[i].second;
    sort(v.begin(), v.end());
    int l = 0, r = 1e9;
    while (l < r) {
        int m = (l + r) / 2;
        if (ck(m))
            r = m;
        else
            l = m + 1;
    }
    cout << (l == 1e9 ? -1 : l) << '\n';
    return;
}
int32_t main() {
#ifdef ONLINE_JUDGE
    ios::sync_with_stdio(false);
    cin.tie(0);
#endif
    // cin >> __t;
    while (__t--)
        solve();
    return 0;
}

全部评论

相关推荐

点赞 评论 收藏
分享
评论
1
1
分享
牛客网
牛客企业服务