题目
T(T<=2e5)组样例,每次给出一个n(n<=2e5)和一个s(s<=2e14),
n个人需要被发钱,第i个人被发的要求满足在[li,ri]之间(1<=li<=ri<=1e9)
总共有s元钱,且保证所有人发下限的钱数时够发,即
求最大的x,使得n个人被发的钱的中位数为x,且s元钱够发(不一定要发完s元钱)
保证所有样例的n之和不超过2e5
思路来源
自己搞的,补之前自己wa的没补的题
原来不补题重新过几个月再看这个题就会了orz
题解
首先二分x,二分可行的原因是,
计ls为右端点小于x的区间段数,rs为左端点大于x的区间段数,
in为左端点小于等于x且右端点大于等于x的区间段数
随着x的增长,ls和rs一增一减的变化是连续的,
必存在一段,这一段的值x满足ls<(n-1)/2且rs<(n-1)/2,
x合法,此时只需判断钱数够不够
保证答案恰有(n-1)/2个<x,剩下的均>=x即可满足中位数的要求,
先用ls和rs的判断来二分x,ls和rs合法后,这些都用左端点最省钱,
需要安排in个区间中的(n-1)/2-ls个区间,
它们直接安排为左端点,且是in中的最小的(n-1)/2-ls个
其余的都安排为中位数x,即可满足要求
如何从in个中找前topk小个区间,考虑对原序列按l排增序,
二分的时候,线性扫两遍区间即可
代码
#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
using namespace std;
typedef long long ll;
const int N=2e5+10;
typedef pair<ll,ll> P;
ll s;
int t,n;
P a[N];
bool ok(int x){
int ls=0,rs=0,in=0;
ll sum=0;
for(int i=1;i<=n;++i){
if(a[i].se<x)ls++,sum+=a[i].fi;
else if(a[i].fi>x)rs++,sum+=a[i].fi;
else in++;
}
if(ls>(n-1)/2)return 0;//小的太多 只能往小
if(rs>(n-1)/2)return 1;//只能往大
for(int i=1;i<=n;++i){
if(a[i].fi<=x && a[i].se>=x){
if(ls<(n-1)/2){
sum+=a[i].fi;
ls++;
}
else{
sum+=x;
}
}
}
if(sum<=s)return 1;//合法 可以往大
return 0;//不合法 只能往小
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%lld",&n,&s);
for(int i=1;i<=n;++i){
scanf("%d%d",&a[i].fi,&a[i].se);
}
sort(a+1,a+n+1);
int L=0,R=1e9;
while(L<=R){
int mid=(L+R)/2;
if(ok(mid))L=mid+1;
else R=mid-1;
}
printf("%d\n",R);
}
return 0;
}