[CF] D. Salary Changing - Educational Codeforces Round 75 (Rated for Div. 2)

https://codeforces.com/contest/1251/problem/D

题目大意

给出奇数 n (<2×105) 个人的薪资范围,其中第 i 个人的范围为 [li,ri](1liri109),以及薪资和的上限 lis2×1014。问当所有人的薪资 xi 符合自己的范围,且和不超过 s 时,中位数最大可以是多少。

简要题解

乍一想因为我们有的是总薪资的上界,那么这个中位数大概是可以二分的。比如当某个方案中位数是 x 则我们可以通过减少某些 x 的值来得出对于 x1 也成立。但是因为下限的存在 x1 其实并不是总能达到的,因此我们选择更改一下二分的判断标准,即把二分判断改为,“当我们尝试将中位数设置到 x 时,是否能得出不小于 x 的中位数”。这样一来,就可以二分了。

另一种思考方式是,首先至少我们的中位数不会小于所有位置都选 li 的结果 x0,显然最优解如果 x>x0 一定在某些位置不是 li,且这些位置一定为 x

判断的过程,对于每一个区间 [li,ri] 若其与 x 不相交,则期望其与中位数无关,则对于它们都会取尽量小的 li 使得其他区间有更多的操作空间。

  1. 如果我们有超过半数的区间已经严格小于 x 则无论剩下的如何大,都不能使得中位数达到或超过 x
  2. 若有超过半数的区间严格大于 x 则显然我们已经得到了不差于 x 的结果。
  3. 其他情况,说明有一些区间可以取到 mid,这里我们需要有足够多的值 >=mid 而其他值尽量小,因此让 li 最小的一些取 li 即可。

复杂度

TO(nlognlogmax

这里如果先把区间对所有 l_i 排序可以去掉每次二分之后对 l_i 的排序的 \log n

SO(n)

代码实现

#include <bits/stdc++.h>
using namespace std;
int io_=[](){ ios::sync_with_stdio(false); cin.tie(nullptr); return 0; }();
using LL = long long;
using ULL = unsigned long long;
using LD = long double;
using PII = pair<int, int>;
using VI = vector<int>;
using MII = map<int, int>;
template<typename T> void cmin(T &x,const T &y) { if(y<x) x=y; }
template<typename T> void cmax(T &x,const T &y) { if(x<y) x=y; }
template<typename T> bool ckmin(T &x,const T &y) {
return y<x ? (x=y, true) : false; }
template<typename T> bool ckmax(T &x,const T &y) {
return x<y ? (x=y, true) : false; }
template<typename T> void cmin(T &x,T &y,const T &z) {// x<=y<=z
if(z<x) { y=x; x=z; } else if(z<y) y=z; }
template<typename T> void cmax(T &x,T &y,const T &z) {// x>=y>=z
if(x<z) { y=x; x=z; } else if(y<z) y=z; }
// mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
// mt19937_64 rnd_64(chrono::system_clock::now().time_since_epoch().count());
/*
---------1---------2---------3---------4---------5---------6---------7---------
1234567890123456789012345678901234567890123456789012345678901234567890123456789
*/
const int INF = 1e9;
void solve() {
int n;
LL s;
cin >> n >> s;
vector<int> li(n), ri(n);
for (int i = 0; i < n; i++) {
cin >> li[i] >> ri[i];
}
auto check = [&](int mid) -> bool {
int cntless = 0, cntmore = 0;
LL sum = 0;
vector<int> left;
for (int i = 0; i < n; i++) {
if (ri[i] < mid) {
sum += li[i];
cntless++;
} else if (mid < li[i]) {
sum += li[i];
cntmore++;
} else {
left.push_back(li[i]);
}
}
if (cntless >= (n + 1) / 2) return false;
if (cntmore >= (n + 1) / 2) return true;
sort(left.begin(), left.end());
int need = n / 2 - cntless;
for (int i = 0; i < need; i++) {
sum += left[i];
}
sum += 1LL * (left.size() - need) * mid;
return sum <= s;
};
int l = 1, r = INF;
int ans = 1, mid;
while (l <= r) {
mid = (l + r) >> 1;
if (check(mid)) {
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
cout << ans << '\n';
}
int main() {
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
Prev: [CF] B. Two Cakes - Educational Codeforces Round 35 (Rated for Div. 2)
Next: [CF] C. Minimize The Integer - Educational Codeforces Round 75 (Rated for Div. 2)

Gitalking ...