[TJOI2013]獎學金
題目描述
小張最近發表了一篇論文,有一個神祕人物要給小張學院發獎學金。小張學院有C名學生,要從中挑出N個。這個神祕人物愛好奇特,他希望得到獎學金的同學的成績的中位數儘可能大,但同時,他們的獎學金總額不能超過F。
輸入格式
第一行包含整數N,C,F。N一定是奇數,N≤C
接下來的C行,每一行描述一個學生的成績和獎學金。0≤成績≤2*10^ 9,0≤獎學金≤10^5
輸出格式
滿足條件的最大中位數。如果無解輸出-1。
題目非常簡短;
可以看出,有兩個限制條件,一個是人數,一個是錢數,然後求最大成績;
看起來毫無頭緒,考慮二分思維(暴力思維),枚舉成績當中位數看是否可行;
但是這題沒有單調性,二分顯然不可以,那麼怎麼枚舉可以降低複雜度呢?
可以發現如果以成績排序,那麼該同學一定在n/2+1到c-n/2只間,因爲要保證左邊和右邊最少爲n/2個人;
只要枚舉 i (n/2+1<=i<=c-n/2)就可以,那這題就轉化爲求1 - i 的長度爲n/2的和的最小值,
求 i - c 的長度爲n/2的和的最小值;
優先隊列就可以了;
代碼:
#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=200100;
const int M=10100;
const LL mod=1e9+7;
int n,c,f;
LL mx1[N],mx2[N];
LL mmax;
priority_queue<LL>qu;
struct Node{
int a;
LL b;
}stu[N];
bool cmp(Node p,Node q){
return p.a<q.a;
}
int main(){
scanf("%d%d%d",&n,&c,&f);
for(int i=1;i<=c;i++) scanf("%d%lld",&stu[i].a,&stu[i].b);
sort(stu+1,stu+1+c,cmp);
for(int i=1;i<=n/2;i++){
mmax+=stu[i].b;
qu.push(stu[i].b);
}
for(int i=n/2+1;i<=c-n/2;i++){
mx1[i]=mmax;
if(stu[i].b<qu.top()){
mmax-=qu.top(),mmax+=stu[i].b;
qu.pop();
qu.push(stu[i].b);
}
}
while(!qu.empty()) qu.pop();
mmax=0;
for(int i=c;i>=c-n/2+1;i--){
mmax+=stu[i].b;
qu.push(stu[i].b);
}
for(int i=c-n/2;i>=n/2+1;i--){
mx2[i]=mmax;
if(stu[i].b<qu.top()){
mmax-=qu.top(),mmax+=stu[i].b;
qu.pop();
qu.push(stu[i].b);
}
}
for(int i=c-n/2;i>=n/2+1;i--){
if(mx1[i]+mx2[i]+stu[i].b<=f){
printf("%d\n",stu[i].a);
return 0;
}
}
printf("-1\n");
return 0;
}