D. Salary Changing(二分,前缀和,贪心,瞎搞)

Salary Changing

Thinking

这道题第一思路就是二分,模拟了一下样例,感觉好像行于是就开始写。

对于二分,我们一定是二分中位数是什么,二分的边界对我们来说是非常重要的,所以我们在二分前有必要确认我们的二分边界,因为一定有 ∑ i = 1 n l i < = s \sum _{i = 1} ^ {n} l_i <= s i=1nli<=s,所以我们对 l l l数组 s o r t sort sort一遍,得到 l e f t = l m i d left = l_{mid} left=lmid,同样的,对 r r r数组 s o r t sort sort一遍,得到 r i g h t = r m i d right = r_{mid} right=rmid,接下来我们就可以开始我们的二分了。

但是这道题的难点不是在二分思想上,而是 j u d g e judge judge函数有点难写:

  • 首先我们对每一个人的 s a l a r y salary salary按照 r r r从小到大排序。
  • 接下来,我们可以通过二分得到 s a l a r y l < = m i d < = s a l a r y r salary_l <= mid <= salary_r salaryl<=mid<=salaryr的人数量 n u m num num,如果 n < n / 2 + 1 n < n / 2 + 1 n<n/2+1这个二分值过大,直接返回false。
  • 否则我们进行贪心的选值,记录下这些点的 s a l a r y l salary_l salaryl,然后进行排序,优先选择小的去补充我们所需的 s a l a r y < m i d salary < mid salary<mid,但是还没有选满的。
  • 接下来就是对于 s a l a r y > = m i d salary >= mid salary>=mid的进行贪心选,假设他的 s a l a r y l > x salary_l > x salaryl>x就选择 s a l a r y l salary_l salaryl,否则的话就是选择 x x x

下面代码中有更详细的描述 j u d g e judge judge函数。

Coding

#include <bits/stdc++.h>
#define mp make_pair
#define pb push_back

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

const double pi = acos(-1.0);
const double eps = 1e-7;
const int inf = 0x3f3f3f3f;

inline ll read() {
    ll f = 1, x = 0;
    char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-')    f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') {
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    return f * x;
}

const int N = 2e5 + 10;

ll L[N], R[N], n, s, m;

struct Node {
    int l, r;
    bool operator < (const Node & t) const {
        return r < t.r;
    }
}a[N];

bool judge(int x) {
    int p = lower_bound(R + 1, R + 1 + n, x) - R;
    int last = n - p + 1;//后面剩下了多少个数,
    if(last < m)    return false;//如果没办法满足 >= x的人数至少有n / 2 + 1个,那么x过大。
    vector<int> v;
    for(int i = p; i <= n; i++) v.pb(a[i].l);
    sort(v.begin(), v.end());
    int need = m - 1 - (p - 1);//前面选择完任然不够,所需要的。
    ll sum = L[p - 1];//一个前缀和数组,看main函数即可理解
    for(int i = 0; i < need; i++) {
        sum += v[i];
        if(v[i] > x)    return false;//一定满足前面的数 <= x,
    }
    for(int i = need; i < v.size(); i++)    sum += max(x, v[i]);
    return sum <= s;
}

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "r", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int _ = read();
    while(_--) {
        n = read(), s = read();
        m = n / 2 + 1;
        for(int i = 1; i <= n; i++) {
            a[i].l = read(), a[i].r = read();
            L[i] = a[i].l, R[i] = a[i].r;
        }
        sort(a + 1, a + 1 + n);
        sort(L + 1, L + 1 + n);
        sort(R + 1, R + 1 + n);
        int l = L[(n >> 1) + 1], r = R[(n >> 1) + 1];
        for(int i = 1; i <= n; i++)
            L[i] = a[i].l + L[i - 1];
        while(l < r) {
            int mid = l + r + 1 >> 1;
            if(judge(mid))  l = mid;
            else r = mid - 1;
        }
        printf("%d\n", l);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值